diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 62be4db..44d1d1c 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -1,238 +1,238 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate) cmake_policy(SET CMP0022 NEW) message("MODULES" ${CMAKE_MODULE_PATH}) # LLVM #====================== FIND_PACKAGE (LLVM REQUIRED) set(LLVM_VERSION ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message("LLVM LIB PATH:" ${LLVM_LIBRARY_DIRS}) message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") INCLUDE_DIRECTORIES(${LLVM_INCLUDE_DIRS}) message(STATUS "INCLUDE DIR: ${LLVM_INCLUDE_DIRS}") add_definitions(${LLVM_DEFINITIONS}) message(STATUS "LLVM DEFS: " ${LLVM_DEFINITIONS}) execute_process( COMMAND llvm-config --libs OUTPUT_VARIABLE LLVM_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "LLVM LIBS: " ${LLVM_LIBS}) # CLANG #====================== set(CLANG_LIBS clangCodeGen clangASTMatchers clangQuery clangTooling clangFrontend clangSerialization clangDriver clangParse clangSema clangAnalysis clangAST clangEdit clangLex clangBasic ) # POTASSCO #====================== set(CLINGO_PATH "/opt/potassco/clingo" CACHE PATH "Path to potassco sources") set(POTASSCO_INCLUDE_PATH ${CLINGO_PATH}/libgringo ${CLINGO_PATH}/libclasp ${CLINGO_PATH}/libclingo ${CLINGO_PATH}/libprogram_opts ${CLINGO_PATH}/liblp ) INCLUDE_DIRECTORIES(${POTASSCO_INCLUDE_PATH}) set(LIBCLASP_LIBS clingo clasp gringo program_opts reify lp ) message("CLASP LIBS: " ${LIBCLASP_LIBS}) # OTHER DEPENDENCIES #=========================== set(JEAYESON_INCLUDE_PATH ${CMAKE_HOME_DIRECTORY}/../vendors/jeayeson/include/ ) INCLUDE_DIRECTORIES(${JEAYESON_INCLUDE_PATH}) # COCO #=========================== set(COCO_EXECUTABLE "" CACHE PATH "Path to coco executable") set(COCO_FRAMES_PATH "" CACHE PATH "Path to coco frames") set(COCO_GRAMMAR_PATH ${CMAKE_HOME_DIRECTORY}/../grammar/) set(COCO_SOURCE_FILES_MAIN ${COCO_GRAMMAR_PATH}/main/Parser.cpp ${COCO_GRAMMAR_PATH}/main/Scanner.cpp ) set(COCO_SOURCE_FILES_MODULES ${COCO_GRAMMAR_PATH}/modules/Parser.cpp ${COCO_GRAMMAR_PATH}/modules/Scanner.cpp ) set(COCO_SOURCE_FILES ${COCO_SOURCE_FILES_MODULES} ${COCO_SOURCE_FILES_MAIN}) INCLUDE_DIRECTORIES(${COCO_GRAMMAR_PATH}) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MAIN} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar main ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/xreate.ATG ) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MODULES} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar modules ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/modules.ATG ) message(STATUS "COCO GRAMMAR BUILD STATUS:" ${COCO_OUTPUT}) # XREATE #====================== set(SOURCE_FILES analysis/temporalseqgraph.cpp pass/cfatemporalseqpass.cpp analysis/cfagraph.cpp pass/cfapass.cpp modules.cpp compilation/interpretation-instructions.cpp ExternLayer.cpp analysis/cfagraph.cpp - compilation/latereasoning.cpp + compilation/latetranscend.cpp analysis/interpretation.cpp query/latex.cpp query/polymorph.cpp compilation/polymorph.cpp aux/latereasoning.cpp compilation/latex.cpp analysis/typeinference.cpp xreatemanager.cpp transcendlayer.cpp analysis/dfagraph.cpp llvmlayer.cpp pass/compilepass.cpp analysis/utils.cpp pass/dfapass.cpp compilation/targetinterpretation.cpp pass/interpretationpass.cpp ast.cpp aux/xreatemanager-decorators.cpp compilation/operators.cpp compilation/transformations.cpp compilation/transformersaturation.cpp pass/versionspass.cpp attachments.cpp compilation/containers.cpp compilation/advancedinstructions.cpp utils.cpp pass/abstractpass.cpp contextrule.cpp query/containers.cpp aux/serialization/expressionserializer.cpp ) set(XREATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ ) INCLUDE_DIRECTORIES(${XREATE_INCLUDE_DIRS}) set(XREATE_PRIVATE_INCLUDE_DIRS ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) add_library(${PROJECT_NAME} SHARED ${COCO_SOURCE_FILES} ${SOURCE_FILES}) target_link_libraries(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) get_directory_property(DEFINITIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) message("definitions all: " ${DEFINITIONS_ALL}) target_compile_definitions(${PROJECT_NAME} INTERFACE ${DEFINITIONS_ALL}) get_directory_property(COMPILATION_OPTIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) message("compilations all: " ${COMPILATION_OPTIONS_ALL}) target_compile_options(${PROJECT_NAME} INTERFACE ${COMPILATION_OPTIONS_ALL}) SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBCLASP_LIBS} ${CLANG_LIBS} ${LLVM_LIBS} tbb boost_system boost_filesystem ) #${CLANG_LIBS} #set (LINK_INTERFACE_LIBRARIES "") # FUNCTION(PREPEND var prefix) # SET(listVar "") # FOREACH(f ${ARGN}) # LIST(APPEND listVar "${prefix}/${f}") # ENDFOREACH(f) # SET(${var} "${listVar}" PARENT_SCOPE) # ENDFUNCTION(PREPEND) #set(COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "-j4") #cotire(xreate) # MACRO (ADD_PCH_RULE _header_filename _src_list) # SET(_gch_filename "${_header_filename}.gch") # LIST(APPEND ${_src_list} ${_gch_filename}) # SET (_args ${CMAKE_CXX_FLAGS}) # LIST(APPEND _args -c ${_header_filename} -o ${_gch_filename}) # GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES) # foreach (_inc ${DIRINC}) # LIST(APPEND _args "-I" ${_inc}) # endforeach(_inc ${DIRINC}) # SEPARATE_ARGUMENTS(_args) # add_custom_command(OUTPUT ${_gch_filename} # COMMAND rm -f ${_gch_filename} # COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args} # DEPENDS ${_header_filename}) # ENDMACRO(ADD_PCH_RULE _header_filename _src_list) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/ast.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/llvmlayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/clasplayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/pass/abstractpass.h SOURCE_FILES) diff --git a/cpp/src/ExternLayer.cpp b/cpp/src/ExternLayer.cpp index 81991bb..03afa74 100644 --- a/cpp/src/ExternLayer.cpp +++ b/cpp/src/ExternLayer.cpp @@ -1,337 +1,334 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * ExternLayer.cpp * * Created on: 4/21/15 * Author: pgess - * - * \file ExternLayer.h - * \brief Support of external C code. Wrapper over Clang */ #include "ExternLayer.h" #include "clang/Tooling/Tooling.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/CodeGenABITypes.h" #include #include #include #include #include #include using namespace std; using namespace clang; using namespace clang::driver; using namespace clang::tooling; using namespace clang::ast_matchers; using namespace llvm; namespace xreate{ class FinderCallbackTypeDecl : public MatchFinder::MatchCallback{ public: QualType typeResult; virtual void run(const MatchFinder::MatchResult &Result) { if (const TypedefDecl * decl = Result.Nodes.getNodeAs("typename")) { typeResult = decl->getUnderlyingType(); } } } ; class FinderCallbackFunction : public MatchFinder::MatchCallback{ public: QualType typeResult; virtual void run(const MatchFinder::MatchResult &Result) { if (const FunctionDecl * decl = Result.Nodes.getNodeAs("function")) { typeResult = decl->getType(); } } } ; void ExternData::addLibrary(Atom&& name, Atom&& package) { __dictLibraries.emplace(name.get(), package.get()); } void ExternData::addIncludeDecl(Expression&& e) { assert(e.op == Operator::LIST); //TODO ?? implement Expression parsing(Array of Expr as vector); for(size_t i = 0, size = e.operands.size(); i < size; ++i) { std::string library = e.bindings.at(i); assert(__dictLibraries.count(library)); std::string package = __dictLibraries.at(library); Expression listHeaders = e.operands.at(i); assert(listHeaders.op == Operator::LIST); std::vector headers; std::transform(listHeaders.operands.begin(), listHeaders.operands.end(), std::inserter(headers, headers.end()), [](const Expression & o) { assert(o.__state == Expression::STRING); return o.getValueString(); }); entries.emplace_back(ExternEntry{package, std::move(headers)}); } } void ExternLayer::addExternalData(const std::vector& data) { entries.insert(entries.end(), data.begin(), data.end()); } ExternLayer::ExternLayer(LLVMLayer *llvm) : __llvm(llvm) { } std::vector ExternLayer::fetchPackageFlags(const ExternEntry& entry) { std::vector args; FILE* flags = popen((string("pkg-config --cflags ") + entry.package).c_str(), "r"); size_t linesize = 0; char* linebuf = 0; ssize_t linelen = 0; while ((linelen = getdelim(&linebuf, &linesize, ' ', flags)) > 0) { if (linebuf[0] == '\n') continue; if (linelen == 1 && linebuf[0] == ' ') continue; //cut unwanted symbols at the end char symbLast = linebuf[linelen - 1 ]; if (symbLast == ' ' || symbLast == '\n') linebuf[linelen - 1] = 0; //print header for debug purposes llvm::outs() << '<' << linebuf << "> "; args.push_back(linebuf); free(linebuf); linebuf = 0; } pclose(flags); return (args); } std::vector ExternLayer::fetchPackageLibs(const ExternEntry& entry) { std::vector libs; FILE* flags = popen((string("pkg-config --libs ") + entry.package).c_str(), "r"); size_t linesize = 0; char* linebuf = 0; ssize_t linelen = 0; while ((linelen = getdelim(&linebuf, &linesize, ' ', flags)) > 0) { if (linebuf[0] == '\n') continue; if (linelen == 1 && linebuf[0] == ' ') continue; //cut unwanted symbols at the end char symbLast = linebuf[linelen - 1 ]; if (symbLast == ' ' || symbLast == '\n') linebuf[linelen - 1] = 0; //cut unwanted symbols at the beginning if (linelen > 1 && linebuf[0] == '-' && linebuf[1] == 'l') { libs.push_back(linebuf + 2); } else { libs.push_back(linebuf); } //print lib name for debug purposes llvm::outs() << '<' << linebuf << "> "; free(linebuf); linebuf = 0; } pclose(flags); return (libs); } void ExternLayer::loadLibraries(vector&& libs) { string msgErr; for (const string& lib : libs) { const string& libName = string("lib") + lib + ".so"; if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently(libName.c_str(), &msgErr)) { llvm::errs() << "\n" << "Loading library " << lib << ". " << msgErr << "\n"; } } } void ExternLayer::init(const AST* root) { addExternalData(root->__externdata); // TODO -EXTERN01.DIP, use default include path from 'clang -xc++ -E' list code; std::vector args{ "-I/usr/include" , "-I/usr/local/include" , "-I/usr/lib64/clang/5.0.1/include" // ,"-I/usr/lib/gcc/x86_64-linux-gnu/4.9/include" // ,"-I/usr/include/x86_64-linux-gnu" }; std::vector libs; boost::format formatInclude("#include \"%1%\""); for(const ExternEntry& entry : entries) { llvm::outs() << "[ExternC] Processing package: " << entry.package << "\n"; llvm::outs() << "[ExternC] args: "; vector&& args2 = fetchPackageFlags(entry); args.insert(args.end(), args2.begin(), args2.end()); for(const string arg : args2) { llvm::outs() << "<" << arg << "> "; } llvm::outs() << "\n[ExternC] libs: "; args2 = fetchPackageLibs(entry); for(const string arg : args2) { llvm::outs() << "<" << arg << "> "; } libs.insert(libs.end(), args2.begin(), args2.end()); llvm::outs() << "\n[ExternC] headers: "; std::transform(entry.headers.begin(), entry.headers.end(), std::inserter(code, code.begin()), [&formatInclude](const string header ) { string line = boost::str(formatInclude % header); llvm::outs() << "<" << line << "> "; return line; }); llvm::outs() << '\n'; } loadLibraries(move(libs)); ast = buildASTFromCodeWithArgs(boost::algorithm::join(code, "\n"), args); __llvm->module->setDataLayout(ast->getASTContext().getTargetInfo().getDataLayout()); __clang.reset(new CompilerInstance()); __clang->createDiagnostics(); __codegen.reset(CreateLLVMCodeGen( __clang->getDiagnostics(), __llvm->module->getName(), __clang->getHeaderSearchOpts(), __clang->getPreprocessorOpts(), clang::CodeGenOptions(), __llvm->llvmContext )); __codegen->Initialize(ast->getASTContext()); }; bool ExternLayer::isPointer(const clang::QualType &t) { const clang::Type * tInfo = t.getTypePtr(); assert(tInfo); return tInfo->isAnyPointerType(); } llvm::Type* ExternLayer::toLLVMType(const clang::QualType& t) { return CodeGen::convertTypeForMemory( __codegen->CGM(), t); } std::vector ExternLayer::getStructFields(const clang::QualType& ty) { clang::QualType t = ty; if (isPointer(ty)) { const clang::PointerType* tPtr = ty->getAs(); t = tPtr->getPointeeType(); } assert(t.getTypePtr()->isRecordType()); const RecordType *record = t->getAsStructureType(); assert(record); std::vector result; //FieldDecl* field: record->getDecl()->fields() for (auto i = record->getDecl()->field_begin(); i != record->getDecl()->field_end(); ++i) { result.push_back(i->getName()); } return result; } bool ExternLayer::isArrayType(const std::string& type){ clang::QualType typeRaw = lookupType(type); if (isPointer(typeRaw)) { const clang::PointerType* typePtr = typeRaw->getAs(); typeRaw = typePtr->getPointeeType(); } return typeRaw->isArrayType(); } bool ExternLayer::isRecordType(const std::string& type){ clang::QualType typeRaw = lookupType(type); if (isPointer(typeRaw)) { const clang::PointerType* typePtr = typeRaw->getAs(); typeRaw = typePtr->getPointeeType(); } return typeRaw->isRecordType(); } clang::QualType ExternLayer::lookupType(const std::string& id) { MatchFinder finder; FinderCallbackTypeDecl callbackTypeDecl; auto matcherTypeDecl = typedefDecl(hasName(id)).bind("typename"); finder.addMatcher(matcherTypeDecl, &callbackTypeDecl); finder.matchAST(ast->getASTContext()); assert(! callbackTypeDecl.typeResult.isNull()); return callbackTypeDecl.typeResult; } llvm::Function* ExternLayer::lookupFunction(const std::string& name) { if (__functions.count(name)) { return __functions.at(name); } MatchFinder finder; FinderCallbackFunction callback; auto matcher = functionDecl(hasName(name)).bind("function"); finder.addMatcher(matcher, &callback); finder.matchAST(ast->getASTContext()); if (callback.typeResult.isNull()) { cout << "[External Layer] " << "Unknown function: " << name << endl; assert(false && "Unknown external function"); } const QualType& tyFuncQual = callback.typeResult; llvm::Type *tyRaw = CodeGen::convertTypeForMemory(__codegen->CGM(), tyFuncQual); llvm::FunctionType* tyRawFunc = llvm::dyn_cast(tyRaw); llvm::Function* function = llvm::Function::Create( tyRawFunc, llvm::GlobalValue::ExternalLinkage, name, __llvm->module.get()); __functions.emplace(name, function); return function; } }//end of xreate namespace diff --git a/cpp/src/ExternLayer.h b/cpp/src/ExternLayer.h index d0f0c33..0bae75e 100644 --- a/cpp/src/ExternLayer.h +++ b/cpp/src/ExternLayer.h @@ -1,67 +1,72 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * ExternLayer.h * * Created on: 4/21/15 * Author: pgess */ +/** + * \file ExternLayer.h + * \brief An external C code interaction support + */ #ifndef XREATE_EXTERNLAYER_H #define XREATE_EXTERNLAYER_H #include "llvmlayer.h" #include #include #include #include "ast.h" #include "clang/AST/ASTContext.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/Lex/PreprocessorOptions.h" namespace clang{ class CodeGenerator; } namespace xreate{ struct ExternData{ void addLibrary(Atom&& name, Atom&& package); void addIncludeDecl(Expression&& e); std::vector entries; std::map __dictLibraries; }; +/** \brief A wrapper over Clang */ class ExternLayer{ public: ExternLayer(LLVMLayer* llvm); void init(const AST* root); llvm::Function* lookupFunction(const std::string& name); clang::QualType lookupType(const std::string& id); std::vector getStructFields(const clang::QualType& ty); llvm::Type* toLLVMType(const clang::QualType& t); bool isPointer(const clang::QualType& t); bool isArrayType(const std::string& type); bool isRecordType(const std::string& type); static std::vector fetchPackageFlags(const ExternEntry& entry); static std::vector fetchPackageLibs(const ExternEntry& entry); private: std::unique_ptr ast; std::unique_ptr __codegen; std::unique_ptr __clang; LLVMLayer* __llvm; std::vector entries; std::map __functions; void addExternalData(const std::vector& data); void loadLibraries(std::vector&& libs); }; } #endif //XREATE_EXTERNLAYER_H diff --git a/cpp/src/analysis/cfagraph.cpp b/cpp/src/analysis/cfagraph.cpp index ef0c679..17639e3 100644 --- a/cpp/src/analysis/cfagraph.cpp +++ b/cpp/src/analysis/cfagraph.cpp @@ -1,191 +1,191 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: CFAGraph.cpp * Author: pgess * * Created on June 27, 2016, 2:09 PM */ /** - * \file cfagraph.h - * \brief Control Flow Analysis(CFA) graph data + * \file cfagraph.h + * \brief Control Flow Analysis(CFA) graph representation */ #include "analysis/cfagraph.h" #include "analysis/utils.h" using namespace xreate::cfa; using namespace std; void CFAGraph::print(std::ostringstream& output) const { const std::string& atomBinding = Config::get("transcend.bindings.function"); const std::string& atomBindingScope = Config::get("transcend.bindings.scope"); output << endl << "%\t\tStatic analysis: CFA" << endl; output << __outputPrecomputed.str(); //show function tags int counterTags = 0; std::ostringstream bufFunctionNames; boost::format formatFunction("function(%1%)."); boost::format formatBind(atomBinding + "(%1%, %2%)."); for (auto function : this->__fnNodes.left) { const auto tags = this->__fnTags.equal_range(function.first); if (tags.first == tags.second) { //no tags bufFunctionNames << "; " << function.second ; continue; } output << formatFunction % (function.second) << std::endl; for (const auto& tag_ : boost::make_iterator_range(tags)) { const Expression& tag = tag_.second; list tagRaw = xreate::analysis::compile(tag); assert(tagRaw.size() == 1); output << formatBind % (function.second) % (tagRaw.front()) << endl; ++counterTags; } } if (bufFunctionNames.tellp()) { output << formatFunction % (bufFunctionNames.str().substr(2)) << std::endl; } if (counterTags == 0) { output << "%no function tags at all" << endl; } //declare scopes boost::format formatScope("scope(0..%1%)."); output << formatScope % (__transcend->getScopesCount() - 1) << std::endl; //show context rules: for (auto rule : this->__contextRules) { output << ContextRule(rule.second).compile(rule.first) << std::endl; }; //show scope tags: counterTags = 0; boost::format formatScopeBind(atomBindingScope + "(%1%, %2%, strong)."); for (auto entry : this->__scopeTags) { ScopePacked scopeId = entry.first; const Expression& tag = entry.second; list tagRaw = xreate::analysis::compile(tag); assert(tagRaw.size() == 1); output << formatScopeBind % scopeId % (tagRaw.front()) << endl; ++counterTags; } if (counterTags == 0) { output << "%scope tags: no tags at all" << endl; } //parent connections //TOTEST CFG parent function boost::format formatFunctionParent("cfa_parent(%1%, function(%2%))."); for (const auto &relation : this->__parentFnRelation) { const string& function = this->__fnNodes.left.at(relation.right); output << formatFunctionParent % relation.left % function << endl; } //TOTEST CFG parent scope boost::format formatScopeParent("cfa_parent(%1%, scope(%2%))."); for (const auto &relation : this->__parentScopeRelation) { output << formatScopeParent % relation.first % relation.second << endl; } //call connections boost::format formatCall("cfa_call(%1%, %2%)."); for (const auto &relation : this->__callRelations) { const ScopePacked scopeFrom = relation.left; const string& functionTo = this->__fnNodes.left.at(relation.right); output << formatCall % (scopeFrom) % (functionTo) << endl; } //function specializations description boost::format formatSpecializations("cfa_function_specializations(%1%, %2%)."); const list& functions = __transcend->ast->getAllFunctions(); for (auto f : functions) { if (f->guard.isValid()) { list guardRaw = xreate::analysis::compile(f->guard); assert(guardRaw.size() == 1); output << formatSpecializations % (f->getName()) % (guardRaw.front()) << endl; } } } void CFAGraph::addFunctionAnnotations(const std::string& fn, const std::map& tags) { unsigned int fid = registerNodeFunction(fn); for (auto& tag : tags) { __fnTags.emplace(fid, tag.second); } } void CFAGraph::addScopeAnnotations(const ScopePacked& scope, const std::vector& tags) { for (Expression tag : tags) { __scopeTags.emplace(scope, tag); } } void CFAGraph::addContextRules(const ScopePacked& scope, const std::vector& rules) { for (Expression rule : rules) { __contextRules.emplace(scope, rule); } } void CFAGraph::addCallConnection(const ScopePacked& callerScope, const std::string& calleeFn) { unsigned int idFuncTo = registerNodeFunction(calleeFn); __callRelations.insert(CALL_RELATIONS::value_type(callerScope, idFuncTo)); } void CFAGraph::addParentConnection(const ScopePacked& scopeEntry, const std::string& fnParent) { __parentFnRelation.insert(PARENT_FUNCTION_RELATIONS::value_type(scopeEntry, registerNodeFunction(fnParent))); } void CFAGraph::addParentConnection(const ScopePacked& scopeChild, const ScopePacked& scopeParent) { __parentScopeRelation.emplace(scopeChild, scopeParent); } unsigned int CFAGraph::registerNodeFunction(const std::string& fname) { auto pos = __fnNodes.left.insert(make_pair(__fnNodes.size(), fname)); return pos.first->first; } void CFAGraph::addScope(CodeScope* scope) { boost::format formatScopeBinding("ast_scope_binding(%1%, %2%, \"%3%\")."); ScopePacked scopeId = __transcend->pack(scope); __scopesCount = max(scopeId + 1, __scopesCount); for (int id = 0, size = scope->__bindings.size(); id < size; ++id) { __outputPrecomputed << formatScopeBinding % scopeId % id % scope->__bindings.at(id) << endl; } } diff --git a/cpp/src/analysis/cfagraph.h b/cpp/src/analysis/cfagraph.h index 543731a..0f5fb0d 100644 --- a/cpp/src/analysis/cfagraph.h +++ b/cpp/src/analysis/cfagraph.h @@ -1,66 +1,67 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: CFAGraph.h * Author: pgess * * Created on June 27, 2016, 2:09 PM */ #ifndef CFAGRAPH_H #define CFAGRAPH_H #include "transcendlayer.h" namespace xreate{ namespace cfa{ -/** \brief Represents CFA analysis data produced by CFAPass */ +/** \brief Represents CFA analysis data produced by CFAPass + */ class CFAGraph: public IAnalysisReport{ friend class TemporalSeqGraph; public: typedef boost::bimap> PARENT_FUNCTION_RELATIONS; PARENT_FUNCTION_RELATIONS __parentFnRelation; std::map __parentScopeRelation; typedef boost::bimap< boost::bimaps::multiset_of, boost::bimaps::multiset_of, boost::bimaps::set_of_relation<> > CALL_RELATIONS; CALL_RELATIONS __callRelations; boost::bimap __fnNodes; std::multimap __fnTags; std::multimap __scopeTags; std::multimap __contextRules; unsigned int __scopesCount = 0; void print(std::ostringstream &output) const override; CFAGraph(TranscendLayer* engine): __transcend(engine){ } void addFunctionAnnotations(const std::string &fn, const std::map &tags); void addScopeAnnotations(const ScopePacked &scope, const std::vector &tags); void addContextRules(const ScopePacked &scope, const std::vector &rules); void addCallConnection(const ScopePacked &callerScope, const std::string &calleeFn); void addParentConnection(const ScopePacked &scopeEntry, const std::string &fnParent); void addParentConnection(const ScopePacked &scopeChild, const ScopePacked &scopeParent); void addScope(CodeScope* scope); private: TranscendLayer* __transcend; std::ostringstream __outputPrecomputed; unsigned int registerNodeFunction(const std::string &fname); }; } } //end of namespace xreate::cfa #endif /* CFAGRAPH_H */ diff --git a/cpp/src/analysis/dfagraph.cpp b/cpp/src/analysis/dfagraph.cpp index aabc49a..7eebd53 100644 --- a/cpp/src/analysis/dfagraph.cpp +++ b/cpp/src/analysis/dfagraph.cpp @@ -1,244 +1,244 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: DFAGraph.h * Author: pgess * */ /** * \file dfagraph.h - * \brief Data Flow Analysis(DFA) graph data + * \brief Data Flow Analysis(DFA) graph representation * */ #include "analysis/dfagraph.h" #include "analysis/utils.h" using namespace std; using namespace xreate::analysis; namespace xreate { namespace dfa { void DFACallInstance::print(std::ostringstream& output) const{ boost::format formatArgs; boost::format formatInstance("dfa_callfn(%1%, %2%)."); switch (type) { case WEAK: formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%))."); break; case STRONG: formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%)).\ndfa_callargs(%1%, %2%, %3%)."); break; } output << formatInstance % analysis::writeSymbolNode(retActual) % fnName << endl; for(std::pair rec: args) { SymbolNode argFormal(rec.first); output << formatArgs % analysis::writeSymbolNode(retActual) % analysis::writeSymbolNode(argFormal) % analysis::writeSymbolNode(rec.second) << endl; } } void DFAGraph::addDependency(const SymbolNode& node, const SymbolNode& subnode){ __dependencies.emplace(node, subnode); if (boost::get(&node)){ __usedSymbols.insert(node); } if (boost::get(&subnode)){ __usedSymbols.insert(node); } } void DFAGraph::printDependencies(std::ostringstream& output) const{ for(const SymbolNode& root: __roots){ printDependency(output, root, root); } } void DFAGraph::printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const { auto range = __dependencies.equal_range(nodeCurrent); for (auto it = range.first; it != range.second; ++it){ if (boost::get(&it->second)){ if (!__usedSymbols.count(it->second)){ printDependency(output, it->second, nodeDependent); continue; } } boost::format formatDependency("dfa_depends(%1%, %2%)."); output << formatDependency % analysis::writeSymbolNode(nodeDependent) % analysis::writeSymbolNode(it->second) << endl; printDependency(output, it->second, it->second); } } void DFAGraph::printInplaceAnnotation(const SymbolNode& node, const Expression& expression) { // write down in-place expression tags: boost::format formatBind("bind(%1%, %2%)."); __usedSymbols.insert(node); for (const string& tag: xreate::analysis::compile(expression)) { __output << formatBind % analysis::writeSymbolNode(node) % tag << endl; } } void DFAGraph::printLateAnnotation(const SymbolNode& node, const Expression& expression, const std::list& symbols, const std::list& domains){ boost::format formatLateAnnotation("late(%1%, (%2%), (%3%), %4%):- %5%."); boost::format formatDom("%1%(%2%)"); std::list exprSerialized = xreate::analysis::compile(expression); assert(exprSerialized.size() == 1); list identSymbols, identNames, domainsSerialised; auto domainI = domains.begin(); for(auto symbol: symbols){ identSymbols.push_back(analysis::writeSymbolNode(symbol.second).str()); identNames.push_back(symbol.first); domainsSerialised.push_back((formatDom % *domainI % symbol.first).str()); ++domainI; } __output << formatLateAnnotation % analysis::writeSymbolNode(node) % boost::algorithm::join(identSymbols, ", ") % boost::algorithm::join(identNames, ", ") % exprSerialized.front() % boost::algorithm::join(domainsSerialised, "; ") << endl; } void DFAGraph::printAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){ __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual); boost::format formatAlias("dfa_alias(%1%, %2%)."); __output << formatAlias % analysis::writeSymbolNode(symbFormal) % analysis::writeSymbolNode(symbActual) << endl; } void DFAGraph::printWeakAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){ __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual); boost::format formatAlias("weak(dfa_alias(%1%, %2%))."); __output << formatAlias % analysis::writeSymbolNode(symbFormal) % analysis::writeSymbolNode(symbActual) << endl; } void DFAGraph::printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet){ boost::format formatRet("dfa_fnret(%1%, %2%)."); __usedSymbols.insert(symbolRet); __output << formatRet % function->getName() % analysis::writeSymbolNode(symbolRet) << endl; __roots.insert(symbolRet); } void DFAGraph::addCallInstance(DFACallInstance&& instance){ __usedSymbols.insert(instance.retActual); for(const auto arg: instance.args){ __usedSymbols.insert(SymbolNode(arg.first)); __usedSymbols.insert(arg.second); } __callInstances.push_back(std::move(instance)); } void DFAGraph::print(std::ostringstream& output) const{ output << endl << "%\t\tStatic analysis: DFA" << endl; //Dependencies printDependencies(output); //Add generated report output << __output.str() << endl; //Call instances for(const DFACallInstance& instance: __callInstances){ instance.print(output); } output << endl; } void DFAGraph::printSymbols(TranscendLayer* transcend){ boost::format formatHint("shint(%1%, \"%2%\")."); for (const SymbolNode& node : __usedSymbols) { __output << "v(" << analysis::writeSymbolNode(node) << "). "; if (const SymbolPacked* symbol = boost::get(&node)){ __output << formatHint % analysis::writeSymbolNode(node) % transcend->getHintForPackedSymbol(*symbol); } __output << endl; } } void DFAGraph::printOperator(Operator op, std::list&& operands, int dataOpListSize){ std::string opStr; switch(op){ case Operator::MAP: opStr = "map"; break; case Operator::FOLD: opStr = "fold"; break; case Operator::LIST: opStr = "list"; break; case Operator::LIST_RANGE: opStr = "list_range"; break; case Operator::INDEX: opStr = "index"; break; default: assert(false); } std::ostringstream bufOperands; for(const SymbolNode& operand: operands){ __usedSymbols.insert(operand); bufOperands << analysis::writeSymbolNode(operand) << ", "; } if(op == Operator::LIST){ bufOperands << dataOpListSize << ", "; } boost::format formatOperator("ast_op_%1%(%2%)."); __output << (formatOperator % opStr % bufOperands.str().substr(0, bufOperands.str().size() - 2)) << endl; } }} //end of namespace xreate::dfa diff --git a/cpp/src/analysis/dfagraph.h b/cpp/src/analysis/dfagraph.h index d8817b0..c1ec4db 100644 --- a/cpp/src/analysis/dfagraph.h +++ b/cpp/src/analysis/dfagraph.h @@ -1,66 +1,66 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: dfa.h * Author: pgess * * Created on June 27, 2016, 1:50 PM */ #ifndef DFA_H #define DFA_H #include "transcendlayer.h" #include namespace xreate { namespace latereasoning { typedef std::pair LateParameter; }} namespace xreate {namespace dfa { enum DFACallInstanceType { STRONG, WEAK }; class DFACallInstance { public: std::string fnName; std::vector> args; SymbolNode retActual; DFACallInstanceType type; void print(std::ostringstream& output) const; }; -/** \brief Holds DFA Analysis report produced by DFAPass */ +/** \brief Represents DFA Analysis report produced by DFAPass */ class DFAGraph : public IAnalysisReport { public: // DFAGraph(TranscendLayer* engine): __transcend(engine){} virtual void print(std::ostringstream& output) const override; void addCallInstance(DFACallInstance && instance); void addDependency(const SymbolNode& node, const SymbolNode& subnodeBlock); void printInplaceAnnotation(const SymbolNode& node, const Expression& expression); void printLateAnnotation(const SymbolNode& node, const Expression& expression, const std::list& symbols, const std::list& domains); void printAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual); void printWeakAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual); void printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet); void printDependencies(std::ostringstream& output) const; void printSymbols(TranscendLayer* transcend); void printOperator(Operator, std::list&& operands, int dataOpListSize = 0); private: mutable std::ostringstream __output; std::list __callInstances; std::unordered_multimap __dependencies; std::unordered_set __usedSymbols; std::unordered_set __roots; void printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const; }; }} // end of namespace xreate::dfa #endif /* DFA_H */ diff --git a/cpp/src/analysis/interpretation.cpp b/cpp/src/analysis/interpretation.cpp index b841a8e..7c18b4e 100644 --- a/cpp/src/analysis/interpretation.cpp +++ b/cpp/src/analysis/interpretation.cpp @@ -1,274 +1,269 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 25, 2018, 3:25 PM - * - * \file interpretation.cpp - * \brief interpretation */ #include "analysis/interpretation.h" using namespace std; namespace xreate{ namespace interpretation{ typedef vector InstancePacked; std::list generateAllInstancesInDomain2(const ExpandedType& domainT) { if(!domainT->isValid()) { return {Expression()}; } assert(domainT->__operator == TypeOperator::VARIANT); std::list results; int variantId = -1; bool flagDomainStateless = std::all_of(domainT->__operands.begin(), domainT->__operands.end(), [](const TypeAnnotation & subdomainT) { return !subdomainT.isValid(); }); for(const TypeAnnotation& subdomainT : domainT->__operands) { ++variantId; if(flagDomainStateless) { Expression result(Operator::VARIANT,{}); result.setValueDouble(variantId); results.push_back(result); continue; } std::list subresults = generateAllInstancesInDomain2(ExpandedType(subdomainT)); for (const Expression& subresult : subresults) { Expression result(Operator::VARIANT,{}); result.setValueDouble(variantId); result.operands.push_back(subresult); results.push_back(result); } } return results; } TypeAnnotation collapseFnGroup(const std::list& symbols) { Gringo::Symbol symbolAny = symbols.front(); size_t operandsCount = symbolAny.args().size; TypeAnnotation resultT; resultT.__operands.reserve(operandsCount); for(size_t operandId = 0; operandId < operandsCount; ++operandId) { std::list column; for(const Gringo::Symbol& row : symbols) { column.push_back(row.args()[operandId]); } TypeAnnotation operandT = collapseColumn(column); resultT.__operands.push_back(operandT); } if(resultT.__operands.size() == 1) { return resultT.__operands.front(); } if(resultT.__operands.size() > 1) { resultT.__operator = TypeOperator::LIST_RECORD; return resultT; } return resultT; } TypeAnnotation collapseColumn(const std::list& symbols) { TypeAnnotation resultT; if(!symbols.size()) return resultT; Gringo::Symbol symbolAny = symbols.front(); switch(symbolAny.type()) { case Gringo::SymbolType::Num: { return TypeAnnotation(TypePrimitive::Num); } case Gringo::SymbolType::Str: { return TypeAnnotation(TypePrimitive::String); } case Gringo::SymbolType::Fun: { map> fnGroups; for(const Gringo::Symbol& row : symbols) { fnGroups[row.name().c_str()].push_back(row); } TypeAnnotation resultT; resultT.__operands.reserve(fnGroups.size()); resultT.bindings.reserve(fnGroups.size()); for(const auto& group : fnGroups) { if(!group.second.size()) continue; TypeAnnotation variantT = collapseFnGroup(group.second); Gringo::Symbol symbolAny = group.second.front(); string variantName = symbolAny.name().c_str(); resultT.fields.push_back(variantName); resultT.__operands.push_back(variantT); } resultT.__operator = TypeOperator::VARIANT; // if(resultT.__operands.size() == 1) { // return resultT.__operands.front(); // } return resultT; } case Gringo::SymbolType::Inf: case Gringo::SymbolType::Special: case Gringo::SymbolType::Sup: { break; } } assert(false); return TypeAnnotation(); } ExpandedType dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend) { assert(t->__operator == TypeOperator::SLAVE); const string& domain = t->__valueCustom; StaticModel model = transcend->query(domain); if(!model.size()) return ExpandedType(TypeAnnotation()); std::list symbols; for(auto row : model) { symbols.push_back(row.second); } return ExpandedType(collapseFnGroup(symbols)); } Expression representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend) { atom.print(std::cout); std::cout<__operator) { case TypeOperator::NONE: { switch(schemaT->__value) { case TypePrimitive::I8: case TypePrimitive::I32: case TypePrimitive::I64: case TypePrimitive::Num: case TypePrimitive::Int: { return Expression(Atom(atom.num())); } case TypePrimitive::String: { return Expression(Atom(atom.string().c_str())); } case TypePrimitive::Invalid: case TypePrimitive::Bool: case TypePrimitive::Float: { assert(false); return Expression(); } } break; } case TypeOperator::SLAVE: { ExpandedType contentT = dereferenceSlaveType(schemaT, transcend); return representTransExpression(atom, contentT, transcend); } case TypeOperator::VARIANT: { map dictVariants; for(size_t variantId = 0; variantId < schemaT->fields.size(); ++variantId) { dictVariants.emplace(schemaT->fields.at(variantId), variantId); } string predicateName = atom.name().c_str(); assert(dictVariants.count(predicateName)); size_t predicateId = dictVariants.at(predicateName); Expression result(Operator::VARIANT,{}); result.op = Operator::VARIANT; result.setValueDouble(predicateId); if(!schemaT->__operands.size()) return result; ExpandedType contentT = schemaT->__operands.at(predicateId).__operator == TypeOperator::SLAVE ? dereferenceSlaveType(ExpandedType(schemaT->__operands.at(predicateId)), transcend) : ExpandedType(schemaT->__operands.at(predicateId)); //edge case, content's type is LIST_NAMED: if (contentT->__operator == TypeOperator::LIST_RECORD) { result.operands.push_back(representTransExpression(atom, contentT, transcend)); } else if(!contentT->isValid()) { return result; } else { assert(atom.args().size); result.operands.push_back(representTransExpression(atom.args()[0], contentT, transcend)); } return result; } case TypeOperator::LIST_RECORD: { const Gringo::SymSpan& operandsRaw = atom.args(); size_t opCount = operandsRaw.size; assert(opCount == schemaT->__operands.size()); size_t operandId = 0; std::vector operands; operands.reserve(opCount); for(const TypeAnnotation operandT : schemaT->__operands) { operands.push_back(representTransExpression(operandsRaw[operandId], ExpandedType(operandT), transcend)); ++operandId; } Expression result(Operator::LIST,{}); result.operands = operands; result.type = schemaT; return result; } case TypeOperator::LIST_ARRAY: case TypeOperator::CALL: case TypeOperator::CUSTOM: case TypeOperator::ACCESS: case TypeOperator::LINK: { assert(false); return Expression(); } } assert(false); return Expression(); } } } //end of xreate namespace diff --git a/cpp/src/analysis/interpretation.h b/cpp/src/analysis/interpretation.h index 7716eb8..8a93e17 100644 --- a/cpp/src/analysis/interpretation.h +++ b/cpp/src/analysis/interpretation.h @@ -1,31 +1,45 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 25, 2018, 3:24 PM - * - * \file interpretation.h - * \brief interpretation + */ + +/** + * \file interpretation.h + * \brief Interpretation related functions */ #ifndef INTERPRETATION_H #define INTERPRETATION_H #include "transcendlayer.h" -namespace xreate{ -namespace interpretation{ +namespace xreate{ namespace interpretation{ + + /** + * \brief Converts a Transcend's fact to a Xreate's expression that can be interpreted further. Supports `query` keyword. + * @param atom Transcend's fact + * @param schemaT Type of the resulting expression + * @param transcend Transcend's instance + * @return converted Transcend' fact in form of Xreate's expression + */ + Expression representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend); -TypeAnnotation collapseColumn(const std::list& symbols); -ExpandedType dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend); -Expression representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend); -std::list generateAllInstancesInDomain2(const ExpandedType& domainT); + /** + * \brief Expands slave type. + * @param t Slave type + * @param transcend Instance of Transcend + * @return The expanded slave type + */ + ExpandedType dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend); + + TypeAnnotation collapseColumn(const std::list& symbols); + std::list generateAllInstancesInDomain2(const ExpandedType& domainT); } } #endif /* INTERPRETATION_H */ diff --git a/cpp/src/analysis/temporalseqgraph.h b/cpp/src/analysis/temporalseqgraph.h index c3672ec..14b88ec 100644 --- a/cpp/src/analysis/temporalseqgraph.h +++ b/cpp/src/analysis/temporalseqgraph.h @@ -1,77 +1,85 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: temporalseqgraph.h * Author: pgess * * Created on February 4, 2019, 4:51 PM */ +/** + * \file temporalseqgraph.h + * \brief Execution Order Graph representation + */ + #ifndef TEMPORALSEQGRAPH_H #define TEMPORALSEQGRAPH_H #include "transcendlayer.h" namespace xreate{ namespace cfa{ enum TemporalOperator {SCOPE, EMPTY, AND, OR}; struct TemporalNode{ ScopePacked scope; TemporalOperator mark; friend std::ostream& operator << (std::ostream& output, const TemporalNode& node); bool operator==(const TemporalNode& other) const { return other.scope == scope && other.mark == mark; } }; struct TemporalGuard{ enum{IN, OUT} mark; unsigned int id; }; typedef std::pair Socket; }} namespace std { bool operator<(const xreate::cfa::TemporalNode& x, const xreate::cfa::TemporalNode& y); } namespace xreate {namespace cfa { - + +/** + * \brief Execution order graph representation. Produced by CFATemporalSeqPass + */ class TemporalSeqGraph: public IAnalysisReport{ typedef boost::bimap< boost::bimaps::multiset_of, boost::bimaps::multiset_of> Graph; public: TemporalSeqGraph(TranscendLayer* transcend); void addSubScopes(const CodeScope*, const std::list&); void addBranchScopes(const CodeScope*, const std::list&); Socket addSocket(const CodeScope*, TemporalOperator mark); TemporalNode insertBefore(TemporalNode node, TemporalOperator mark); TemporalNode insertAfter(TemporalNode node, TemporalOperator mark); void connect(TemporalNode, TemporalNode); void connectGuarded(const Socket& from, const Socket& to); Socket getFnSocket(ManagedFnPtr calleeFn); Socket getUncertainFnSocket(const std::string& calleeName, const std::list& candidates); bool isOrdered(const ScopePacked& scopeAfter, const ScopePacked& scopeBefore) const; bool isOrdered(const TemporalNode& nodeAfter, const TemporalNode& nodeBefore, std::set&) const; void print(std::ostringstream &output) const override; private: Graph graph; std::multimap> graphGuarded; ScopePacked __idNextVacant = 0; unsigned int __guardNextVacant = 0; TranscendLayer* __transcend; std::map __cacheFnSockets; std::map __cacheUncertainFnSockets; }; } } //end of namespace xreate::cfa #endif /* TEMPORALSEQGRAPH_H */ diff --git a/cpp/src/analysis/typeinference.cpp b/cpp/src/analysis/typeinference.cpp index 3583aca..c6d2b5a 100644 --- a/cpp/src/analysis/typeinference.cpp +++ b/cpp/src/analysis/typeinference.cpp @@ -1,87 +1,98 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * typeinference.cpp * * Author: pgess * Created on April 16, 2017, 10:13 AM */ /** * \file typeinference.h * \brief Type inference analysis */ #include "typeinference.h" #include "llvmlayer.h" #include "transcendlayer.h" #include "llvm/IR/Function.h" #include "llvm/IR/DerivedTypes.h" using namespace std; namespace xreate{ namespace typeinference{ //TODO type conversion: //a) automatically expand types int -> bigger int; int -> floating //b) detect exact type of `num` based on max used numeral / function type //c) warning if need to truncate (allow/dissallow based on annotations) - + llvm::Value* doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder) { if(tyTarget->isIntegerTy() && source->getType()->isIntegerTy()) { llvm::IntegerType* tyTargetInt = llvm::dyn_cast(tyTarget); llvm::IntegerType* tySourceInt = llvm::dyn_cast(source->getType()); if(tyTargetInt->getBitWidth() < tySourceInt->getBitWidth()) { return builder.CreateCast(llvm::Instruction::Trunc, source, tyTarget); } if(tyTargetInt->getBitWidth() > tySourceInt->getBitWidth()) { return builder.CreateCast(llvm::Instruction::SExt, source, tyTarget); } } if(source->getType()->isIntegerTy() && tyTarget->isFloatingPointTy()) { return builder.CreateCast(llvm::Instruction::SIToFP, source, tyTarget); } if (source->getType()->isStructTy() && tyTarget->isIntegerTy()){ llvm::StructType* sourceST = llvm::cast(source->getType()); if(sourceST->getNumElements() == 1) { llvm::Value* sourceElRaw = builder.CreateExtractValue(source, llvm::ArrayRef({0})); return doAutomaticTypeConversion(sourceElRaw, tyTarget, builder); } } return source; } +/** + * \brief Performs basic type inference to deduce the type of the given expression + * + * Tries several strategies in the following order: + * - Looks at expression's type if it has one. + * - Looks at Attachment if it has one. This allows assign expression's type by analyses done elsewhere. + * - For a number literal assumes i32. + * + * \param expression Infers the given expression's type. + * \param ast AST instance. + */ ExpandedType getType(const Expression& expression, const AST& ast) { if(expression.type.isValid()) { return ast.expandType(expression.type); } if(expression.__state == Expression::IDENT) { Symbol s = Attachments::get(expression); return getType(CodeScope::getDefinition(s), ast); } if(Attachments::exists(expression)) { return Attachments::get(expression); } if(expression.__state == Expression::NUMBER) { return ExpandedType(TypeAnnotation(TypePrimitive::I32)); } assert(false && "Type can't be determined for an expression"); } } } //end of namespace xreate::typeinference diff --git a/cpp/src/analysis/typeinference.h b/cpp/src/analysis/typeinference.h index df6ce78..1990995 100644 --- a/cpp/src/analysis/typeinference.h +++ b/cpp/src/analysis/typeinference.h @@ -1,31 +1,36 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: typeinference.h * Author: pgess * * Created on April 16, 2017, 10:17 AM */ #ifndef TYPEINFERENCE_H #define TYPEINFERENCE_H #include "ast.h" #include "llvm/IR/IRBuilder.h" namespace llvm{ class Value; class Type; }; -namespace xreate{ -namespace typeinference{ +namespace xreate{ namespace typeinference{ +/** + * \brief Casts the given variable to the given type + * \param source The variable that needs casting + * \param tyTarget The type to cast to + * \param builder Instance of llvm's IRBuilder + */ llvm::Value* doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder); ExpandedType getType(const Expression& expression, const AST& ast); } }//namespace xreate::typeinference #endif /* TYPEINFERENCE_H */ diff --git a/cpp/src/analysis/utils.cpp b/cpp/src/analysis/utils.cpp index 471e24d..1d8df75 100644 --- a/cpp/src/analysis/utils.cpp +++ b/cpp/src/analysis/utils.cpp @@ -1,159 +1,159 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * aux.cpp * * Author: pgess */ /** - * \file aux.h - * \brief Data representation in ASP format ready for use by reasoner + * \file src/analysis/utils.h + * \brief Various reasoning related utilities */ #include "utils.h" #include -namespace xreate { namespace analysis { - using namespace std; -list -multiplyLists(list> &&lists) { - typedef list StringList; - assert(lists.size()); - StringList result(*lists.begin()); - lists.pop_front(); - - boost::format concat("%s, %s"); - for (StringList &list: lists) { - StringList::const_iterator end = result.end(); - for (StringList::iterator expr1I = result.begin(); expr1I != end; ++expr1I) { - if (list.size() == 0) continue; - - StringList::const_iterator expr2I = list.begin(); - for (int expr2No = 0, size = list.size() - 1; expr2No < size; ++expr2No, ++expr1I) - result.push_back(str(concat %(*expr1I) %(*expr2I))); - - *expr1I = str(concat %(*expr1I) %(*expr2I)); - } - } - - return result; -} +namespace xreate { namespace analysis { std::list compile(const Expression &e){ list result; switch (e.op) { case Operator::CALL: { if(!e.operands.size()){ result.push_back(e.getValueString()); break; } std::list> operands; std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()), [](const Expression &e) { return compile(e); }); list &&operands_ = multiplyLists(std::move(operands)); result.push_back(boost::str(boost::format("%1%(%2%)") % (e.getValueString()) % (boost::algorithm::join(operands_, ", ")))); break; } case Operator::NEG: { assert(e.operands.size() == 1); const Expression &op = e.operands.at(0); list &&rawOp = compile(op); assert(rawOp.size() == 1); result.push_back((boost::format("not %1%")%(rawOp.front())).str()); break; }; case Operator::INVALID: { switch (e.__state) { case Expression::IDENT: result.push_back(e.getValueString()); break; case Expression::NUMBER: result.push_back(to_string(e.getValueDouble())); break; default: assert(true); } break; } default: break; } assert(result.size()); return result; } +list +multiplyLists(list> &&lists) { + typedef list StringList; + assert(lists.size()); + StringList result(*lists.begin()); + lists.pop_front(); + + boost::format concat("%s, %s"); + for (StringList &list: lists) { + StringList::const_iterator end = result.end(); + for (StringList::iterator expr1I = result.begin(); expr1I != end; ++expr1I) { + if (list.size() == 0) continue; + + StringList::const_iterator expr2I = list.begin(); + for (int expr2No = 0, size = list.size() - 1; expr2No < size; ++expr2No, ++expr1I) + result.push_back(str(concat %(*expr1I) %(*expr2I))); + + *expr1I = str(concat %(*expr1I) %(*expr2I)); + } + } + + return result; +} + std::list compileNeg(const Expression &e){ list result; switch (e.op) { case Operator::IMPL: { assert(e.__state == Expression::COMPOUND); assert(e.operands.size() == 2); list operands1 = compile(e.operands.at(0)); list operands2 = compile(e.operands.at(1)); boost::format formatNeg("%1%, not %2%"); for (const auto &op1: operands1) for (const auto &op2: operands2) { result.push_back(boost::str(formatNeg %(op1) % (op2))); } break; } case Operator::NEG: { assert(e.operands.size() == 1); const Expression &op = e.operands.at(0); list &&rawOp = compile(op); assert(rawOp.size() == 1); result.push_back(rawOp.front()); break; }; default: assert(true); } return result; } //NOTE: Any changes should be reflected in ParseImplAtom, // ParseImplAtom class VisitorFormatSymbol: public boost::static_visitor { public: boost::format operator()(const SymbolPacked& node) const { boost::format formatSymbNamed("s(%1%,%2%,%3%)"); return formatSymbNamed % node.identifier % node.version % node.scope ; } boost::format operator()(const SymbolAnonymous& node) const { boost::format formatSymbAnonymous("a(%1%)"); return formatSymbAnonymous % node.id; } }; boost::format writeSymbolNode(const SymbolNode& symbol){ return boost::apply_visitor(VisitorFormatSymbol(), symbol); } }} //end of xreate::analysis diff --git a/cpp/src/analysis/utils.h b/cpp/src/analysis/utils.h index 70c2dc1..82c325b 100644 --- a/cpp/src/analysis/utils.h +++ b/cpp/src/analysis/utils.h @@ -1,31 +1,36 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: aux.h * Author: pgess * * Created on June 26, 2016, 6:49 PM */ #ifndef AUX_H #define AUX_H #include "ast.h" #include "transcendlayer.h" #include #include namespace xreate { namespace analysis { +/** + * \brief Compiles Xreate expression into ASP format recognizable by an external ASP solver. + * @param e expression + * @return textual expression's representation in an ASP format. + */ std::list compile(const Expression &e); std::list compileNeg(const Expression &e); std::list multiplyLists(std::list> &&lists); boost::format writeSymbolNode(const SymbolNode& symbol); }} //end of xreate::analysis #endif /* AUX_H */ diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp index 8b1cf6a..2c24707 100644 --- a/cpp/src/ast.cpp +++ b/cpp/src/ast.cpp @@ -1,977 +1,971 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: ast.cpp */ -/** - * \file ast.h - * \brief Syntax Tree and related code - * - * \sa xreate::AST - */ - #include "ast.h" #include "ExternLayer.h" #include "analysis/typeinference.h" #include #include //TODO BDecl. forbid multiple body declaration (ExprTyped) namespace std { std::size_t hash::operator()(xreate::ScopedSymbol const& s) const { return s.id ^ (s.version << 2); } bool equal_to::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const { return __x.id == __y.id && __x.version == __y.version; } size_t hash::operator()(xreate::Symbol const& s) const { return hash()(s.identifier) ^ ((long int) s.scope << 1); } bool equal_to::operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const { return __x == __y; }; } using namespace std; namespace xreate { Atom::Atom(const std::wstring& value) { __value = wstring_to_utf8(value); } Atom::Atom(std::string && name) : __value(name) { } const std::string& Atom::get() const { return __value; } Atom::Atom(wchar_t* value) { //DEBT reconsider number literal recognition __value = wcstol(value, 0, 10); } Atom::Atom(int value) : __value(value) { } double Atom::get()const { return __value; } Atom::Atom(const std::wstring& value) { assert(value.size() >= 2); __value = wstring_to_utf8(value.substr(1, value.size() - 2)); } Atom::Atom(std::string && name) : __value(name) {} const std::string& Atom::get() const { return __value; } +/** \brief xreate::Expression static information*/ class ExpressionHints { public: static bool isStringValueValid(const Expression& e) { switch (e.__state) { case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: return true; case Expression::NUMBER: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::CALL: return true; default: return false; } } } return false; } static bool isDoubleValueValid(const Expression& e) { switch (e.__state) { case Expression::NUMBER: return true; case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::VARIANT: return true; default: return false; } } } return false; } }; class TypesResolver { private: const AST* ast; std::map scope; std::map signatures; ExpandedType expandType(const TypeAnnotation &t, const std::vector &args = std::vector()) { return TypesResolver(ast, scope, signatures)(t, args); } std::vector expandOperands(const std::vector& operands) { std::vector pack; pack.reserve(operands.size()); std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()), [this](const TypeAnnotation & t) { return expandType(t); }); return pack; } public: TypesResolver(const AST* root, const std::map& scopeOuter = std::map(), std::map signaturesOuter = std::map()) : ast(root), scope(scopeOuter), signatures(signaturesOuter) { } ExpandedType operator()(const TypeAnnotation &t, const std::vector &args = std::vector()) { //assert(args.size() == t.bindings.size()); // invalid number of arguments for (size_t i = 0; i < args.size(); ++i) { scope[t.bindings.at(i)] = args.at(i); } switch (t.__operator) { case TypeOperator::LIST_ARRAY: { assert(t.__operands.size() == 1); Expanded elTy = expandType(t.__operands.at(0)); return ExpandedType(TypeAnnotation(tag_array, elTy, 0)); } case TypeOperator::LIST_RECORD: { std::vector&& packOperands = expandOperands(t.__operands); auto typNew = TypeAnnotation(TypeOperator::LIST_RECORD, move(packOperands)); typNew.fields = t.fields; return ExpandedType(move(typNew)); }; case TypeOperator::VARIANT: { std::vector&& packOperands = expandOperands(t.__operands); auto typNew = TypeAnnotation(TypeOperator::VARIANT, move(packOperands)); typNew.fields = t.fields; return ExpandedType(move(typNew)); }; case TypeOperator::CALL: { std::string alias = t.__valueCustom; //find in local scope: TypeAnnotation ty; if (scope.count(alias)) { ty = scope.at(alias); } else if (ast->__indexTypeAliases.count(alias)) { ty = ast->__indexTypeAliases.at(alias); } else { assert(false && "Undefined or external type"); } std::vector&& operands = expandOperands(t.__operands); TypeAnnotation signature(TypeOperator::CALL, move(operands)); signature.__valueCustom = alias; if (signatures.count(signature)) { auto link = TypeAnnotation(TypeOperator::LINK,{}); link.conjuctionId = signatures.at(signature); return ExpandedType(move(link)); } int cid = signatures.size(); signatures[signature] = cid; TypeAnnotation tyResult = expandType(ty, operands); tyResult.conjuctionId = cid; return ExpandedType(move(tyResult)); }; case TypeOperator::CUSTOM: { if (signatures.count(t)) { return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t})); } signatures.emplace(t, signatures.size()); std::string alias = t.__valueCustom; //find in local scope: if (scope.count(alias)) { return expandType(scope.at(alias)); } //find in general scope: if (ast->__indexTypeAliases.count(alias)) { return expandType(ast->__indexTypeAliases.at(t.__valueCustom)); } //if type is unknown keep it as is. return ExpandedType(TypeAnnotation(t)); }; case TypeOperator::ACCESS: { std::string alias = t.__valueCustom; ExpandedType tyAlias = ExpandedType(TypeAnnotation()); //Find in local scope: if (scope.count(alias)) { tyAlias = expandType(scope.at(alias)); //Find in global scope: } else if ((ast->__indexTypeAliases.count(alias))) { tyAlias = expandType(ast->__indexTypeAliases.at(alias)); } else { assert(false && "Undefined or external type"); } assert(tyAlias->__operator == TypeOperator::LIST_RECORD); for (const string& field : t.fields) { auto fieldIt = std::find(tyAlias->fields.begin(), tyAlias->fields.end(), field); assert(fieldIt != tyAlias->fields.end() && "unknown field"); int fieldId = fieldIt - tyAlias->fields.begin(); tyAlias = expandType(tyAlias->__operands.at(fieldId)); } return tyAlias; } case TypeOperator::NONE: { return ExpandedType(TypeAnnotation(t)); } case TypeOperator::SLAVE: { return ExpandedType(t); } default: assert(false); } assert(false); return ExpandedType(TypeAnnotation()); } }; TypeAnnotation::TypeAnnotation() : __operator(TypeOperator::NONE), __value(TypePrimitive::Invalid) { } TypeAnnotation::TypeAnnotation(TypePrimitive typ) : __value(typ) { } TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list operands) : __operator(op), __operands(operands) { } TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector&& operands) : __operator(op), __operands(operands) { } TypeAnnotation::TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size) : TypeAnnotation(TypeOperator::LIST_ARRAY,{typ}) { __size = size; } bool TypeAnnotation::isValid() const { return !(__value == TypePrimitive::Invalid && __operator == TypeOperator::NONE); } bool TypeAnnotation::operator<(const TypeAnnotation& t) const { if (__operator != t.__operator) return __operator < t.__operator; if (__operator == TypeOperator::NONE) return __value < t.__value; if (__operator == TypeOperator::CALL || __operator == TypeOperator::CUSTOM || __operator == TypeOperator::ACCESS) { if (__valueCustom != t.__valueCustom) return __valueCustom < t.__valueCustom; } return __operands < t.__operands; } /* TypeAnnotation (struct_tag, std::initializer_list) {} */ void TypeAnnotation::addBindings(std::vector>&& params) { bindings.reserve(bindings.size() + params.size()); std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()), [](const Atom& ident) { return ident.get(); }); } void TypeAnnotation::addFields(std::vector>&& listFields) { fields.reserve(fields.size() + listFields.size()); std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()), [](const Atom& ident) { return ident.get(); }); } unsigned int Expression::nextVacantId = 0; Expression::Expression(const Atom& number) : Expression() { __state = NUMBER; op = Operator::INVALID; __valueD = number.get(); } Expression::Expression(const Atom& a) : Expression() { __state = STRING; op = Operator::INVALID; __valueS = a.get(); } Expression::Expression(const Atom &ident) : Expression() { __state = IDENT; op = Operator::INVALID; __valueS = ident.get(); } Expression::Expression(const Operator &oprt, std::initializer_list params) : Expression() { __state = COMPOUND; op = oprt; if (op == Operator::CALL) { assert(params.size() > 0); Expression arg = *params.begin(); assert(arg.__state == Expression::IDENT); __valueS = std::move(arg.__valueS); operands.insert(operands.end(), params.begin() + 1, params.end()); return; } operands.insert(operands.end(), params.begin(), params.end()); } void Expression::setOp(Operator oprt) { op = oprt; switch (op) { case Operator::INVALID: __state = INVALID; break; default: __state = COMPOUND; break; } } void Expression::addArg(Expression &&arg) { operands.push_back(arg); } void Expression::addTags(const std::list tags) const { std::transform(tags.begin(), tags.end(), std::inserter(this->tags, this->tags.end()), [](const Expression & tag) { return make_pair(tag.getValueString(), tag); }); } void Expression::addBindings(std::initializer_list> params) { addBindings(params.begin(), params.end()); } void Expression::bindType(TypeAnnotation t) { type = move(t); } void Expression::addBlock(ManagedScpPtr scope) { blocks.push_back(scope.operator->()); } const std::vector& Expression::getOperands() const { return operands; } double Expression::getValueDouble() const { return __valueD; } const std::string& Expression::getValueString() const { return __valueS; } void Expression::setValue(const Atom&& v) { __valueS = v.get(); } void Expression::setValueDouble(double value) { __valueD = value; } bool Expression::isValid() const { return (__state != INVALID); } bool Expression::isDefined() const { return (__state != BINDING && __state != INVALID); } Expression::Expression() : __state(INVALID), op(Operator::INVALID), id(nextVacantId++) { } namespace details { namespace inconsistent { AST::AST() { Attachments::init(); Attachments::init(); Attachments::init(); Attachments::init(); } void AST::addInterfaceData(const ASTInterface& interface, Expression&& data) { __interfacesData.emplace(interface, move(data)); } void AST::addDFAData(Expression &&data) { __dfadata.push_back(data); } void AST::addExternData(ExternData &&data) { __externdata.insert(__externdata.end(), data.entries.begin(), data.entries.end()); } void AST::add(Function* f) { __functions.push_back(f); __indexFunctions.emplace(f->getName(), __functions.size() - 1); } void AST::add(MetaRuleAbstract *r) { __rules.push_back(r); } void AST::add(TypeAnnotation t, Atom alias) { if (t.__operator == TypeOperator::VARIANT) { for (int i = 0, size = t.fields.size(); i < size; ++i) { __dictVariants.emplace(t.fields[i], make_pair(t, i)); } } __indexTypeAliases.emplace(alias.get(), move(t)); } ManagedScpPtr AST::add(CodeScope* scope) { this->__scopes.push_back(scope); return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes); } std::string AST::getModuleName() { const std::string name = "moduleTest"; return name; } ManagedPtr AST::findFunction(const std::string& name) { int count = __indexFunctions.count(name); if (!count) { return ManagedFnPtr::Invalid(); } assert(count == 1); auto range = __indexFunctions.equal_range(name); return ManagedPtr(range.first->second, &this->__functions); } std::list AST::getAllFunctions() const { const size_t size = __functions.size(); std::list result; for (size_t i = 0; i < size; ++i) { result.push_back(ManagedFnPtr(i, &this->__functions)); } return result; } //TASK select default specializations std::list AST::getFunctionSpecializations(const std::string& fnName) const { auto functions = __indexFunctions.equal_range(fnName); std::list result; std::transform(functions.first, functions.second, inserter(result, result.end()), [this](auto f) { return ManagedFnPtr(f.second, &this->__functions); }); return result; } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__functions); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__scopes); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__rules); } void AST::recognizeVariantConstructor(Expression& function) { assert(function.op == Operator::CALL); std::string variant = function.getValueString(); if (!__dictVariants.count(variant)) { return; } auto record = __dictVariants.at(variant); const TypeAnnotation& typ = record.first; function.op = Operator::VARIANT; function.setValueDouble(record.second); function.type = typ; } Atom AST::recognizeVariantConstructor(Atom ident) { std::string variant = ident.get(); assert(__dictVariants.count(variant) && "Can't recognize variant constructor"); auto record = __dictVariants.at(variant); return Atom(record.second); } void AST::postponeIdentifier(CodeScope* scope, const Expression& id) { bucketUnrecognizedIdentifiers.emplace(scope, id); } void AST::recognizePostponedIdentifiers() { for (const auto& identifier : bucketUnrecognizedIdentifiers) { if (!identifier.first->recognizeIdentifier(identifier.second)) { //exception: Ident not found std::cout << "Unknown symbol: " << identifier.second.getValueString() << std::endl; assert(false && "Symbol not found"); } } } xreate::AST* AST::finalize() { //all finalization steps: recognizePostponedIdentifiers(); return reinterpret_cast (this); } } } //namespace details::incomplete Expanded AST::findType(const std::string& name) { // find in general scope: if (__indexTypeAliases.count(name)) return expandType(__indexTypeAliases.at(name)); //if type is unknown keep it as is. TypeAnnotation t(TypeOperator::CUSTOM,{}); t.__valueCustom = name; return ExpandedType(move(t)); } Expanded AST::expandType(const TypeAnnotation &t) const { return TypesResolver(this)(t); } ExpandedType AST::getType(const Expression& expression) { return typeinference::getType(expression, *this); } Function::Function(const Atom& name) : __entry(new CodeScope(0)) { __name = name.get(); } void Function::addTag(Expression&& tag, const TagModifier mod) { string name = tag.getValueString(); __tags.emplace(move(name), move(tag)); } const std::map& Function::getTags() const { return __tags; } CodeScope* Function::getEntryScope() const { return __entry; } void Function::addBinding(Atom && name, Expression&& argument, const VNameId hintBindingId) { __entry->addBinding(move(name), move(argument), hintBindingId); } const std::string& Function::getName() const { return __name; } ScopedSymbol CodeScope::registerIdentifier(const Expression& identifier, const VNameId hintBindingId) { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); auto result = __identifiers.emplace(identifier.getValueString(), hintBindingId? hintBindingId: __identifiers.size() + 1); return { result.first->second, version }; } bool CodeScope::recognizeIdentifier(const Expression& identifier) const { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); const std::string& name = identifier.getValueString(); //search identifier in the current block if (__identifiers.count(name)) { VNameId id = __identifiers.at(name); Symbol s; s.identifier = ScopedSymbol{id, version}; s.scope = const_cast (this); Attachments::put(identifier, s); return true; } //search in the parent scope if (__parent) { return __parent->recognizeIdentifier(identifier); } return false; } ScopedSymbol CodeScope::getSymbol(const std::string& alias) { assert(__identifiers.count(alias)); VNameId id = __identifiers.at(alias); return {id, versions::VERSION_NONE }; } void CodeScope::addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId) { argument.__state = Expression::BINDING; __bindings.push_back(var.getValueString()); ScopedSymbol binding = registerIdentifier(var, hintBindingId); __declarations[binding] = move(argument); } Symbol CodeScope::addDefinition(Expression&& var, Expression&& body) { ScopedSymbol s = registerIdentifier(var); __declarations[s] = move(body); return Symbol{s, this}; } CodeScope::CodeScope(CodeScope* parent) : __parent(parent) { } CodeScope::~CodeScope() { } void CodeScope::setBody(const Expression &body) { assert(__declarations.count(ScopedSymbol::RetSymbol)==0 && "Attempt to reassign scope body"); __declarations[ScopedSymbol::RetSymbol] = body; } const Expression& CodeScope::getBody() const{ return __declarations.at(ScopedSymbol::RetSymbol); } const Expression& CodeScope::getDefinition(const Symbol& symbol, bool flagAllowUndefined){ const CodeScope* self = symbol.scope; return self->getDefinition(symbol.identifier, flagAllowUndefined); } const Expression& CodeScope::getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined) const{ static Expression expressionInvalid; if (!__declarations.count(symbol)){ if (flagAllowUndefined) return expressionInvalid; assert(false && "Symbol's declaration not found"); } return __declarations.at(symbol); } void RuleArguments::add(const Atom &arg, DomainAnnotation typ) { emplace_back(arg.get(), typ); } void RuleGuards::add(Expression&& e) { push_back(e); } MetaRuleAbstract:: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards) : __args(std::move(args)), __guards(std::move(guards)) { } MetaRuleAbstract::~MetaRuleAbstract() { } RuleWarning:: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message) : MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) { } RuleWarning::~RuleWarning() { } void RuleWarning::compile(TranscendLayer& layer) { //TODO restore addRuleWarning //layer.addRuleWarning(*this); } bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id < s2.id) || (s1.id == s2.id && s1.version < s2.version); } bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id == s2.id) && (s1.version == s2.version); } bool operator<(const Symbol& s1, const Symbol& s2) { return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier); } bool operator==(const Symbol& s1, const Symbol& s2) { return (s1.scope == s2.scope) && (s1.identifier == s2.identifier); } bool operator<(const Expression&a, const Expression&b) { if (a.__state != b.__state) return a.__state < b.__state; assert(a.__state != Expression::INVALID); switch (a.__state) { case Expression::IDENT: case Expression::STRING: return a.getValueString() < b.getValueString(); case Expression::NUMBER: return a.getValueDouble() < b.getValueDouble(); case Expression::COMPOUND: { assert(a.blocks.size() == 0); assert(b.blocks.size() == 0); if (a.op != b.op) { return a.op < b.op; } bool flagAValid = ExpressionHints::isStringValueValid(a); bool flagBValid = ExpressionHints::isStringValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueString() != b.getValueString()) { return a.getValueString() < b.getValueString(); } } flagAValid = ExpressionHints::isDoubleValueValid(a); flagBValid = ExpressionHints::isDoubleValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueDouble() != b.getValueDouble()) { return a.getValueDouble() < b.getValueDouble(); } } if (a.operands.size() != b.operands.size()) { return (a.operands.size() < b.operands.size()); } for (size_t i = 0; i < a.operands.size(); ++i) { bool result = a.operands[i] < b.operands[i]; if (result) return true; } return false; } case Expression::BINDING: case Expression::INVALID: assert(false); } return false; } bool Expression::operator==(const Expression& other) const { if (this->__state != other.__state) return false; if (ExpressionHints::isStringValueValid(*this)) { if (this->__valueS != other.__valueS) return false; } if (ExpressionHints::isDoubleValueValid(*this)) { if (this->__valueD != other.__valueD) return false; } if (this->__state != Expression::COMPOUND) { return true; } if (this->op != other.op) { return false; } if (this->operands.size() != other.operands.size()) { return false; } for (size_t i = 0; ioperands.size(); ++i) { if (!(this->operands[i] == other.operands[i])) return false; } assert(!this->blocks.size()); assert(!other.blocks.size()); return true; } const ScopedSymbol ScopedSymbol::RetSymbol = ScopedSymbol{0, versions::VERSION_NONE}; } //end of namespace xreate diff --git a/cpp/src/ast.h b/cpp/src/ast.h index 59bdb5b..5fee1f6 100644 --- a/cpp/src/ast.h +++ b/cpp/src/ast.h @@ -1,740 +1,745 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: ast.h */ +/** + * \file ast.h + * \brief A syntax tree representation and related code + * + * \sa xreate::AST + */ + #ifndef AST_H #define AST_H #include "attachments.h" #include #include #include #include #include #include #include #include "utils.h" #include namespace llvm { class Value; } namespace xreate { struct ScopedSymbol; struct Symbol; } namespace std { template<> struct hash { std::size_t operator()(xreate::ScopedSymbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const; }; template<> struct hash { size_t operator()(xreate::Symbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const; }; } namespace xreate { struct String_t { }; struct Identifier_t { }; struct Number_t { }; struct Type_t { }; template class Atom { }; //DEBT store line:col for all atoms/identifiers template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; template<> class Atom { public: Atom(wchar_t* value); Atom(int value); double get()const; private: double __value; }; template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; enum class TypePrimitive { Invalid, Bool, I8, I32, I64, Num, Int, Float, String }; enum class TypeOperator { NONE, CALL, CUSTOM, VARIANT, LIST_ARRAY, LIST_RECORD, ACCESS, LINK, SLAVE }; struct llvm_array_tag { }; struct struct_tag { }; const llvm_array_tag tag_array = llvm_array_tag(); const struct_tag tag_struct = struct_tag(); /** - * \brief Represents type to support type system + * \brief A type representation to support type system * - * This class represents type in denormalized form, i.e. without arguments and aliases substitution + * The class represents type in a denormalized form, i.e. with no arguments and aliases substitution * \sa AST::expandType() */ class TypeAnnotation { public: TypeAnnotation(); TypeAnnotation(const Atom& typ); TypeAnnotation(TypePrimitive typ); TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size); TypeAnnotation(TypeOperator op, std::initializer_list operands); TypeAnnotation(TypeOperator op, std::vector&& operands); void addBindings(std::vector>&& params); void addFields(std::vector>&& listFields); bool operator<(const TypeAnnotation& t) const; // TypeAnnotation (struct_tag, std::initializer_list); bool isValid() const; TypeOperator __operator = TypeOperator::NONE; std::vector __operands; TypePrimitive __value; std::string __valueCustom; int conjuctionId = -1; //conjunction point id (relevant for recursive types) uint64_t __size = 0; std::vector fields; std::vector bindings; private: }; enum class Operator { INVALID, UNDEF, ADD, SUB, MUL, DIV, EQU, NE, NEG, LSS, LSE, GTR, GTE, LIST, LIST_RANGE, CALL, CALL_INTRINSIC, QUERY, QUERY_LATE, IMPL/* implication */, MAP, FOLD, INF, INDEX, IF, SWITCH, SWITCH_VARIANT, SWITCH_LATE, CASE, CASE_DEFAULT, LOGIC_AND, CONTEXT_RULE, VARIANT, SEQUENCE }; class Function; class AST; class CodeScope; class MetaRuleAbstract; typedef ManagedPtr ManagedFnPtr; typedef ManagedPtr ManagedScpPtr; typedef ManagedPtr ManagedRulePtr; const ManagedScpPtr NO_SCOPE = ManagedScpPtr(UINT_MAX, 0); /** - * \brief Represents every instruction in Xreate's syntax tree - * \attention In case of any changes update xreate::ExpressionHints auxiliary helper as well + * \brief AST node to represent a single instruction or an annotation + * \attention In case of any changes update \ref xreate::ExpressionHints auxiliary helper as well * - * Expression is generic building block of syntax tree able to hold node data - * as well as child nodes as operands. Not only instructions use expression for representation in syntax tree - * but annotation as well. + * %Expression is a generic building block of syntax tree which is able to hold node data + * along with child nodes as operands. * - * Additionally, `types` as a special kind of annotations use Expression-like data structure TypeAnnotation + * \note For types the %expression-like data structure \ref TypeAnnotation is used rather than Expression itself. * \sa xreate::AST, xreate::TypeAnnotation */ -// struct Expression { friend class CodeScope; friend class TranscendLayer; friend class CFAPass; friend class ExpressionHints; Expression(const Operator &oprt, std::initializer_list params); Expression(const Atom& ident); Expression(const Atom& number); Expression(const Atom& a); Expression(); void setOp(Operator oprt); void addArg(Expression&& arg); void addBindings(std::initializer_list> params); void bindType(TypeAnnotation t); template void addBindings(InputIt paramsBegin, InputIt paramsEnd); void addTags(const std::list tags) const; void addBlock(ManagedScpPtr scope); const std::vector& getOperands() const; double getValueDouble() const; void setValueDouble(double value); const std::string& getValueString() const; void setValue(const Atom&& v); bool isValid() const; bool isDefined() const; bool operator==(const Expression& other) const; /** * \brief is it string, number, compound operation and so on */ enum { INVALID, COMPOUND, IDENT, NUMBER, STRING, BINDING } __state = INVALID; /** * \brief Valid for compound State. Holds type of compound operator */ Operator op; /** * \brief Unique id to identify expression within syntax tree */ unsigned int id; /** * \brief Exact meaning depends on particular instruction * \details As an example, named lists/structs hold field names in bindings */ std::vector bindings; std::map __indexBindings; /** * \brief Holds child instructions as arguments */ std::vector operands; /** * \brief Holds type of instruction's result */ TypeAnnotation type; /** * \brief Holds additional annotations */ mutable std::map tags; /** * \brief Child code blocks * \details For example, If statement holds TRUE-branch as first and FALSE-branch as second block here */ std::list blocks; private: std::string __valueS; double __valueD; static unsigned int nextVacantId; }; bool operator<(const Expression&, const Expression&); template void Expression::addBindings(InputIt paramsBegin, InputIt paramsEnd) { size_t index = bindings.size(); std::transform(paramsBegin, paramsEnd, std::inserter(bindings, bindings.end()), [&index, this] (const Atom atom) { std::string key = atom.get(); this->__indexBindings[key] = index++; return key; }); } typedef std::list ExpressionList; enum class TagModifier { NONE, ASSERT, REQUIRE }; enum class DomainAnnotation { FUNCTION, VARIABLE }; class RuleArguments : public std::vector> { public: void add(const Atom& name, DomainAnnotation typ); }; class RuleGuards : public std::vector { public: void add(Expression&& e); }; class TranscendLayer; class LLVMLayer; class MetaRuleAbstract { public: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards); virtual ~MetaRuleAbstract(); virtual void compile(TranscendLayer& layer) = 0; protected: RuleArguments __args; RuleGuards __guards; }; class RuleWarning : public MetaRuleAbstract { friend class TranscendLayer; public: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message); virtual void compile(TranscendLayer& layer); ~RuleWarning(); private: std::string __message; Expression __condition; }; typedef unsigned int VNameId; namespace versions { typedef int VariableVersion; const VariableVersion VERSION_NONE = -2; const VariableVersion VERSION_INIT = 0; } template<> struct AttachmentsDict { typedef versions::VariableVersion Data; static const unsigned int key = 6; }; struct ScopedSymbol { VNameId id; versions::VariableVersion version; static const ScopedSymbol RetSymbol; }; struct Symbol { ScopedSymbol identifier; const CodeScope * scope; }; struct IdentifierSymbol{}; struct SymbolAlias{}; template<> struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 7; }; template<> struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 9; }; typedef std::pair Tag; bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator<(const Symbol& s1, const Symbol& s2); bool operator==(const Symbol& s1, const Symbol& s2); /** - * \brief Represents code block and single scope of visibility + * \brief AST node to represent a single code block/a scope of visibility * - * Holds single expression as a *body* and set of variable assignments(declarations) used in body's expression + * Holds a single expression as a `body` along with set of variable assignments(declarations) used in body's expression. * \sa xreate::AST */ class CodeScope { friend class Function; friend class PassManager; public: CodeScope(CodeScope* parent = 0); ~CodeScope(); /** \brief Set expression as a body */ void setBody(const Expression& body); /** \brief Returns current code scope body */ const Expression& getBody() const; /** \brief Adds variable definition to be used in body as well as in other declarations */ Symbol addDefinition(Expression&& var, Expression&& body); /** \brief Returns symbols' definition */ static const Expression& getDefinition(const Symbol& symbol, bool flagAllowUndefined = false); const Expression& getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined = false) const; /** \brief Adds variable defined elsewhere */ void addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId = 0); std::vector __bindings; std::map __identifiers; CodeScope* __parent; //TODO move __definitions to SymbolsAttachments data //NOTE: definition of return type has index 0 std::unordered_map __declarations; std::vector tags; std::vector contextRules; private: ScopedSymbol registerIdentifier(const Expression& identifier, const VNameId hintBindingId = 0); public: bool recognizeIdentifier(const Expression& identifier) const; ScopedSymbol getSymbol(const std::string& alias); }; /** - * \brief Represents single function in Xreate's syntax tree + * \brief AST node to represent a single function * - * Holds an entry code scope and `guardContext` required for function to operate + * Holds an `__entry` entry code scope along with `guard` to denote the different specializations. * \sa xreate::AST */ class Function { friend class Expression; friend class CodeScope; friend class AST; public: Function(const Atom& name); /** * \brief Adds function arguments */ void addBinding(Atom && name, Expression&& argument, const VNameId hintBindingId=0); /** * \brief Adds additional function annotations */ void addTag(Expression&& tag, const TagModifier mod); const std::string& getName() const; const std::map& getTags() const; CodeScope* getEntryScope() const; CodeScope* __entry; std::string __name; bool isPrefunction = false; //SECTIONTAG adhoc Function::isPrefunction flag Expression guard; + private: - std::map __tags; }; class ExternData; struct ExternEntry { std::string package; std::vector headers; }; typedef Expanded ExpandedType; struct TypeInferred{}; template<> struct AttachmentsDict { typedef ExpandedType Data; static const unsigned int key = 11; }; enum ASTInterface { CFA, DFA, Extern, Adhoc }; struct FunctionSpecialization { std::string guard; size_t id; }; struct FunctionSpecializationQuery { std::unordered_set context; }; template<> struct AttachmentsId{ static unsigned int getId(const Expression& expression){ return expression.id; } }; template<> struct AttachmentsId{ static unsigned int getId(const Symbol& s){ return s.scope->__declarations.at(s.identifier).id; } }; template<> struct AttachmentsId{ static unsigned int getId(const ManagedFnPtr& f){ const Symbol symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; return AttachmentsId::getId(symbolFunction); } }; template<> struct AttachmentsId{ static unsigned int getId(const CodeScope* scope){ const Symbol symbolScope{ScopedSymbol::RetSymbol, scope}; return AttachmentsId::getId(symbolScope); } }; template<> struct AttachmentsId{ static unsigned int getId(const unsigned int id){ return id; } }; class TypesResolver; namespace details { namespace inconsistent { - /** - * \brief Syntax tree under construction in inconsistent form - * - * Represents Syntax Tree under construction(**inconsistent state**). - * \attention Clients should use rather xreate::AST unless client's code explicitly works with Syntax Tree during construction. - * - * Typically instance only created by xreate::XreateManager and filled in by Parser - * \sa xreate::XreateManager::prepare(std::string&&) - */ +/** + * \brief AST in an inconsistent form during construction + * + * Represents AST under construction(**inconsistent state**). + * \attention Clients should use rather xreate::AST unless client's code explicitly works with Syntax Tree during construction. + * + * Typically an instance is created by xreate::XreateManager only and filled out by the parser + * \sa xreate::XreateManager::prepare(std::string&&) + */ class AST { friend class xreate::TypesResolver; public: AST(); /** * \brief Adds new function to AST * \param f Function to register */ void add(Function* f); /** * \brief Adds new declarative rule to AST * \param r Declarative Rule */ void add(MetaRuleAbstract* r); /** \brief Registers new code block */ ManagedScpPtr add(CodeScope* scope); /** * \brief Add new type to AST * @param t Type definition * @param alias Typer name */ void add(TypeAnnotation t, Atom alias); /** \brief Current module's name */ std::string getModuleName(); /** * \brief Looks for function with given name * \param name Function name to find * \note Requires that only one function exists under given name * \return Found function */ ManagedPtr findFunction(const std::string& name); /** \brief Returns all function in AST */ std::list getAllFunctions() const; /** * \brief Returns all specializations of a function with a given name * \param fnName function to find * \return list of found function specializations */ std::list getFunctionSpecializations(const std::string& fnName) const; /** * \return First element in Functions/Scopes/Rules list depending on template parameter * \tparam Target either Function or CodeScope or MetaRuleAbstract */ template ManagedPtr begin(); /** * \brief Performs all necessary steps after AST is built * - * Performs all finzalisation steps and move AST into consistent state represented by xreate::AST + * Performs all finalization steps and moves AST into consistent state represented by xreate::AST * \sa xreate::AST * \return AST in consistent state */ xreate::AST* finalize(); typedef std::multimap FUNCTIONS_REGISTRY; std::vector __externdata; std::list __dfadata; //TODO move to more appropriate place std::list __rawImports; //TODO move to more appropriate place std::multimap __interfacesData; //TODO CFA data here. private: std::vector __rules; std::vector __functions; std::vector __scopes; FUNCTIONS_REGISTRY __indexFunctions; protected: std::map __indexTypeAliases; public: /** * \brief Stores DFA scheme for later use by DFA Pass * - * Treats expression as a DFA scheme and feeds to a DFA Pass later - * \paramn Expression DFA Scheme + * Treats expression as a DFA scheme and feeds to the DFA Pass later + * \param data DFA Scheme * \sa xreate::DFAPass */ void addDFAData(Expression&& data); /** \brief Stores data for later use by xreate::ExternLayer */ void addExternData(ExternData&& data); /** * \brief Generalized function to store particular data for later use by particular pass * \param interface Particular Interface * \param data Particular data */ void addInterfaceData(const ASTInterface& interface, Expression&& data); /**\name Symbols Recognition */ ///@{ public: //TODO revisit enums/variants, move to codescope /** * \brief Tries to find out whether expression is Variant constructor */ void recognizeVariantConstructor(Expression& function); Atom recognizeVariantConstructor(Atom ident); private: std::map> __dictVariants; public: std::set> bucketUnrecognizedIdentifiers; public: /** * \brief Postpones unrecognized identifier for future second round of recognition * \param scope Code block identifier is encountered * \param id Identifier */ void postponeIdentifier(CodeScope* scope, const Expression& id); /** \brief Second round of identifiers recognition done right after AST is fully constructed */ void recognizePostponedIdentifiers(); ///@} }; template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); } } // namespace details::incomplete /** - * \brief Xreate's Syntax Tree in consistent state + * \brief AST in a consistent state * - * Syntax Tree has two mutually exclusive possible states: - * - inconsistent state while AST is under construction. Represented by xreate::details::inconsistent::AST - * - consistent state when AST is built and finalize() is done. + * AST has two mutually exclusive possible states: + * - an inconsistent state while AST is under construction. Represented by xreate::details::inconsistent::AST + * - a consistent state when AST is built and finalize() is invoked. * - * This class represents consistent state and should be used everywhere unless client's code explicitly works with AST under construction. - * Consistent AST enables access to additional functions(currently related to type management). + * This class represents a consistent state and should be used by clients unless client's code explicitly works with AST under construction. + * Consistent AST enables access to additional functions(such as type management). * \sa xreate::details::inconsistent::AST */ class AST : public details::inconsistent::AST { public: AST() : details::inconsistent::AST() {} /** * \brief Computes fully expanded form of type by substituting all arguments and aliases * \param t Type to expand * \return Expdanded or normal form of type * \sa TypeAnnotation */ ExpandedType expandType(const TypeAnnotation &t) const; /** * Searches type by given name * \param name Typename to search * \return Expanded or normal form of desired type * \note if type name is not found returns new undefined type with this name */ ExpandedType findType(const std::string& name); /** * Invokes Type Inference Analysis to find out expanded(normal) form expressions's type * \sa typeinference.h * \param expression * \return Type of expression */ ExpandedType getType(const Expression& expression); }; } #endif // AST_H diff --git a/cpp/src/attachments.cpp b/cpp/src/attachments.cpp index a9f19f3..2113cf4 100644 --- a/cpp/src/attachments.cpp +++ b/cpp/src/attachments.cpp @@ -1,20 +1,15 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: attachments.cpp * Date: 3/15/15 */ -/** - * \file attachments.h - * \brief Attachments support: mechanism to attach additional data to AST nodes - */ - #include "attachments.h" using namespace xreate; std::vector Attachments::__storage = std::vector(); diff --git a/cpp/src/attachments.h b/cpp/src/attachments.h index a9bc42b..8ef3663 100644 --- a/cpp/src/attachments.h +++ b/cpp/src/attachments.h @@ -1,179 +1,183 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: attachments.h * Date: 3/15/15 */ +/** + * \file attachments.h + * \brief The attachments support: a mechanism to attach additional data to AST nodes + */ + #ifndef _XREATE_ATTACHMENTS_H_ #define _XREATE_ATTACHMENTS_H_ #include #include #include #include namespace xreate { - //Attachments dictionary + /** \brief Attachments dictionary */ template struct AttachmentsDict { // typedef void Data; // static const unsigned int key (next vacant id - 12); // Defined attachments: //----------------------------------------------------- // 1 containers::Implementation // 3 interpretation::InterpretationData // 5 interpretation::FunctionInterpretationData // 6 VariableVersion // 7 IdentifierSymbol // 8 versions::VersionImposedDependency // 9 SymbolAlias // 11 TypeInferred }; template struct AttachmentsId{ //static unsigned int getId(const Object& object); }; - template class IAttachmentsContainer{ protected: virtual bool __exists(const unsigned int object)=0; virtual Data& __get(const unsigned int object)=0; virtual void __put(const unsigned int object, Data data)=0; public: template bool exists(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __exists(id); } template Data& get(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __get(id); } template Data get(const Id& object, const Data& dataDefault){ unsigned int id = AttachmentsId::getId(object); if (! __exists(id)){ return dataDefault; } return __get(id); } template void put(const Id& object, Data data){ unsigned int id = AttachmentsId::getId(object); __put(id, data); } virtual ~IAttachmentsContainer(){}; }; template class AttachmentsContainerDefault: public IAttachmentsContainer{ private: std::unordered_map __data; virtual bool __exists(const unsigned int id){ return __data.count(id); } virtual Data& __get(const unsigned int id){ return __data.at(id); } virtual void __put(const unsigned int id, Data data){ auto result = __data.emplace(id, data); assert(result.second); } public: std::unordered_map& getRawStorage() { return __data; } }; - +/** \brief Implements %AST attachments mechanism to facilitate data sharing among different analyzers and compilation phases */ class Attachments{ private: static std::vector __storage; template using Data = typename AttachmentsDict::Data; public: template static bool exists(const Id& object) { assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->exists(object); } template static Data& get(const Id& object){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object); } template static Data get(const Id& object, const Data& dataDefault){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object, dataDefault); } template static void put(const Id& object, Data data){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); self->put(object, data); } template static void init(){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = new AttachmentsContainerDefault>(); } template static void init(IAttachmentsContainer>* container){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = container; } }; } #endif //_XREATE_ATTACHMENTS_H_ diff --git a/cpp/src/aux/latereasoning.cpp b/cpp/src/aux/latereasoning.cpp index c70ef88..a14143e 100644 --- a/cpp/src/aux/latereasoning.cpp +++ b/cpp/src/aux/latereasoning.cpp @@ -1,57 +1,54 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * Created on June 7, 2018, 6:01 PM - * - * \file latereasoning.cpp - * \brief Late reasoninf support */ - + #include "aux/latereasoning.h" #include "analysis/interpretation.h" #include "ast.h" using namespace std; namespace xreate{ namespace latereasoning{ LateAnnotation::LateAnnotation(const Gringo::Symbol& symbolStatic) { guardedContent.push_back(pair, Gringo::Symbol>({}, symbolStatic)); } boost::optional LateAnnotation::select(const std::list& keys, AST* root, TranscendLayer* transcend) const { for(const auto& entry : guardedContent) { const std::list& guardsExpected = entry.first; auto keyPSIt = guardKeys.begin(); auto keyActualIt = keys.begin(); bool result = true; for(const Gringo::Symbol& guardExpectedGS : guardsExpected) { auto keyS = transcend->unpack(*keyPSIt); const ExpandedType& keyT = root->getType(CodeScope::getDefinition(keyS)); const ExpandedType& keyTPlain = keyT->__operator == TypeOperator::SLAVE ? interpretation::dereferenceSlaveType(keyT, transcend) : keyT; Expression guardExpectedE = interpretation::representTransExpression( guardExpectedGS, keyTPlain, transcend); if(!(guardExpectedE == *keyActualIt)) { result = false; break; } ++keyActualIt; ++keyPSIt; } if(!result) continue; return entry.second; } return boost::none; } } } diff --git a/cpp/src/aux/latereasoning.h b/cpp/src/aux/latereasoning.h index a20dbe1..e6ba460 100644 --- a/cpp/src/aux/latereasoning.h +++ b/cpp/src/aux/latereasoning.h @@ -1,84 +1,91 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * \file latereasoning.h - * \brief Late reasoning support - * * Author: pgess - * * Created on June 2, 2018, 1:08 PM */ + /** + * \file src/aux/latereasoning.h + * \brief Late reasoning support + */ + #ifndef LATEREASONING_H #define LATEREASONING_H #include "transcendlayer.h" namespace xreate{ namespace latereasoning{ +/** \brief Represents Late Annotation, i.e. an annotation with late or runtime defined parameters*/ struct LateAnnotation{ LateAnnotation(){} LateAnnotation(const Gringo::Symbol& symbolStatic); std::list, Gringo::Symbol>> guardedContent; std::list guardKeys; boost::optional select(const std::list& keys, AST* root, TranscendLayer* transcend) const; }; +/** \brief Represents a group of all late annotations attached to a specific target */ struct LateAnnotationsGroup{ std::unordered_map annotations; }; typedef std::map LateModel; +/** + * \brief Decorates \ref TranscendLayer to support late annotations processing + * \extends TranscendLayer + */ template class LateReasoningTranscendDecorator: public Parent{ public: const LateAnnotationsGroup& queryLate(const std::string& alias) const{ static LateAnnotationsGroup groupInvalid; if(!__modelLate.count(alias)) return groupInvalid; return __modelLate.at(alias); } protected: virtual bool processSolution(Gringo::Model const &model) override{ const std::string& atomLateStatement = "late"; for(const Gringo::Symbol& atom: model.atoms(clingo_show_type_atoms)){ std::string atomName(atom.name().c_str()); if(atomName == atomLateStatement){ //late atom's format: (Target, (tuple of keys), (tuple of values), late-annotation) auto atomLate = TranscendLayer::parse, std::list, Gringo::Symbol>(atom); const std::string& atomAlias = std::get<3>(atomLate).name().c_str(); addLateAtom(atomAlias, std::get<0>(atomLate), std::get<3>(atomLate), std::get<1>(atomLate), std::get<2>(atomLate)); } } return Parent::processSolution(model); } private: std::map __modelLate; void addLateAtom(const std::string& alias, const Gringo::Symbol& annId, const Gringo::Symbol& content, const std::list& guardKeys, const std::list& guardValues){ LateAnnotationsGroup& group = __modelLate[alias]; LateAnnotation& annotation = group.annotations[annId]; if (annotation.guardedContent.empty()){ annotation.guardKeys = guardKeys; } annotation.guardedContent.push_back(std::make_pair(guardValues, content)); } }; }} // end of xreate::latereasoning #endif /* LATEREASONING_H */ diff --git a/cpp/src/aux/transcend-decorators.h b/cpp/src/aux/transcend-decorators.h index 321b66c..018e790 100644 --- a/cpp/src/aux/transcend-decorators.h +++ b/cpp/src/aux/transcend-decorators.h @@ -1,34 +1,38 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 7, 2018, 6:47 PM - * + */ + +/** * \file transcend-decorators.h - * \brief TranscendLevel decorators management + * \brief \ref xreate::TranscendLayer decorators management */ #include "transcendlayer.h" #include "aux/latereasoning.h" #ifndef TRANSCEND_DECORATORS_H #define TRANSCEND_DECORATORS_H namespace xreate { + /** + * \brief Default \ref xreate::TranscendLayer functionality + * \extends xreate::TranscendLayer + */ typedef latereasoning::LateReasoningTranscendDecorator DefaultTranscendLayerImpl; struct LateReasoningTranscendDecoratorTag; template<> struct DecoratorsDict{ typedef latereasoning::LateReasoningTranscendDecorator result; }; } #endif /* TRANSCEND_DECORATORS_H */ diff --git a/cpp/src/aux/xreatemanager-decorators.h b/cpp/src/aux/xreatemanager-decorators.h index d1cfe71..e8bc213 100644 --- a/cpp/src/aux/xreatemanager-decorators.h +++ b/cpp/src/aux/xreatemanager-decorators.h @@ -1,38 +1,40 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: xreatemanager-decorators.h * Author: pgess * * Created on July 16, 2017, 4:37 PM */ #ifndef XREATEMANAGER_DECORATORS_H #define XREATEMANAGER_DECORATORS_H #include "xreatemanager.h" namespace xreate{ +/** \brief Simple \ref xm_adapt "XreateManager's" backend intended for inheritance, does not provide much functionality. */ class XreateManagerDecoratorBase: public details::tier2::XreateManager{ public: virtual void initPasses() override; virtual void analyse(); virtual void* run(){}; public: virtual void prepareCode(std::string&& code); virtual void prepareCode(FILE* code); }; +/** \brief \ref xm_adapt "XreateManager's" backend intended to initialize all builtin passes. */ class XreateManagerDecoratorFull: public XreateManagerDecoratorBase{ public: virtual void initPasses() override; void* run(); }; } #endif /* XREATEMANAGER_DECORATORS_H */ diff --git a/cpp/src/aux/xreatemanager-modules.h b/cpp/src/aux/xreatemanager-modules.h index 003d780..f47ca72 100644 --- a/cpp/src/aux/xreatemanager-modules.h +++ b/cpp/src/aux/xreatemanager-modules.h @@ -1,124 +1,124 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: PassManagerModular.h * Author: pgess * * Created on June 22, 2017, 5:32 PM */ /** * \file xreatemanager-modules.h - * \brief XreateManager's decorator to support [Modules](/w/concepts/modules/). + * \brief XreateManager's decorator to support [Modules](/d/syntax/modules/). */ #ifndef PASSMANAGERMODULAR_H #define PASSMANAGERMODULAR_H #include "ast.h" #include "modules.h" #include "modules/Parser.h" #include "main/Parser.h" namespace xreate{namespace modules { template -/** \brief PassManager decorator to add [Modules Concept](/w/concepts/modules/) support +/** \brief XreateManager decorator to support [Modules](/d/syntax/modules/). * - * Scanning of source file looking for other modules requirements. - * Finds and connects other modules to satisfy module's requirements - * - * \sa ModulesSolver, ModulesRegistry, ModuleRecord + * Scans source code looking for other modules requirements. + * Finds and connects other modules to satisfy a current module's requirements + * \extends XreateManager + * \sa ModulesSolver, ModuleRecord */ class XreateManagerDecoratorModules: public Parent{ public: XreateManagerDecoratorModules(){} void prepareCode(std::string&& code) override { Scanner scannerModules(reinterpret_cast(code.c_str()), code.size()); std::list listIncludedFiles; parseModulesGrammar(scannerModules, listIncludedFiles); grammar::main::Scanner scannerMain(reinterpret_cast(code.c_str()), code.size()); parseMainGrammar(scannerMain, listIncludedFiles); } void prepareCode(FILE* code) override { Scanner scannerModules(code); std::list listIncludedFiles; parseModulesGrammar(scannerModules, listIncludedFiles); grammar::main::Scanner scannerMain(code); parseMainGrammar(scannerMain, listIncludedFiles); } private: void parseModulesGrammar(Scanner& scanner, std::list& listIncludedFiles){ ModulesSolver solver; Parser parser(&scanner); parser.Parse(); parser.module.__path = ""; solver.init("", parser.module); std::list modulesExternal = solver.run(parser.module); std::string programBase = solver.__program.str(); for (const std::string module: modulesExternal){ parseModulesGrammar(module, programBase, listIncludedFiles); } } void parseModulesGrammar(const std::string& path, std::string base, std::list& listIncludedFiles){ FILE* input = fopen(path.c_str(), "r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); parser.module.__path = path; fclose(input); ModulesSolver solver; solver.init(base, parser.module); std::list&& modulesExternal = solver.run(parser.module); std::string programBase = solver.__program.str(); for (const std::string module: modulesExternal){ parseModulesGrammar(module, programBase, listIncludedFiles); } listIncludedFiles.push_back(path); } void parseMainGrammar(grammar::main::Scanner& scanner, std::list& listIncludedFiles){ details::inconsistent::AST* ast = new AST; grammar::main::Parser parser(&scanner); parser.root = ast; parser.Parse(); assert(!parser.errors->count && "Parser errors"); for (auto file: listIncludedFiles){ FILE* fileContent = fopen(file.c_str(), "r"); grammar::main::Scanner scanner(fileContent); grammar::main::Parser parser(&scanner); parser.root = ast; parser.Parse(); fclose(fileContent); assert(!parser.errors->count && "Parser errors"); } PassManager::prepare(ast->finalize()); } }; }} //end namespace xreate::modules #endif /* PASSMANAGERMODULAR_H */ diff --git a/cpp/src/compilation/advancedinstructions.cpp b/cpp/src/compilation/advancedinstructions.cpp index 787126c..b6ca440 100644 --- a/cpp/src/compilation/advancedinstructions.cpp +++ b/cpp/src/compilation/advancedinstructions.cpp @@ -1,470 +1,465 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: InstructionsAdvanced.cpp * Author: pgess * * Created on June 26, 2016, 6:00 PM */ -/** - * \file advanced.h - * \brief Compilation of statements that require more than one LLVM instruction - */ - #include "analysis/typeinference.h" #include "compilation/advancedinstructions.h" #include "compilation/containers.h" #include "compilation/transformersaturation.h" #include "query/containers.h" #include "llvmlayer.h" #include "ast.h" using namespace std; using namespace llvm; using namespace xreate; using namespace xreate::containers; using namespace xreate::compilation; #define NAME(x) (hintRetVar.empty()? x : hintRetVar) #define UNUSED(x) (void)(x) #define EXPAND_CONTEXT \ LLVMLayer* llvm = context.pass->man->llvm; \ compilation::ICodeScopeUnit* scope = context.scope; \ compilation::IFunctionUnit* function = context.function; AdvancedInstructions::AdvancedInstructions(compilation::Context ctx) : context(ctx), tyNum(static_cast (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Num))))) { } llvm::Value* AdvancedInstructions::compileMapSolidOutput(const Expression &expr, const std::string hintRetVar) { EXPAND_CONTEXT UNUSED(scope); //initialization Symbol symbolIn = Attachments::get(expr.getOperands()[0]); ImplementationRec implIn = containers::Query::queryImplementation(symbolIn).extract(); // impl of input list size_t size = implIn.size; CodeScope* scopeLoop = expr.blocks.front(); std::string varEl = scopeLoop->__bindings[0]; Iterator* it = Iterator::create(context, symbolIn); llvm::Value *rangeFrom = it->begin(); llvm::Value *rangeTo = it->end(); //definitions ArrayType* tyNumArray = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::Num, size)))); llvm::IRBuilder<> &builder = llvm->builder; llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "loop", function->raw); llvm::BasicBlock *blockBeforeLoop = builder.GetInsertBlock(); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "postloop", function->raw); Value* dataOut = llvm->builder.CreateAlloca(tyNumArray, ConstantInt::get(tyNum, size), NAME("map")); // * initial check Value* condBefore = builder.CreateICmpSLE(rangeFrom, rangeTo); builder.CreateCondBr(condBefore, blockLoop, blockAfterLoop); // create PHI: builder.SetInsertPoint(blockLoop); llvm::PHINode *stateLoop = builder.CreatePHI(tyNum, 2, "mapIt"); stateLoop->addIncoming(rangeFrom, blockBeforeLoop); // loop body: Value* elIn = it->get(stateLoop, varEl); compilation::ICodeScopeUnit* scopeLoopUnit = function->getScopeUnit(scopeLoop); scopeLoopUnit->bindArg(elIn, move(varEl)); Value* elOut = scopeLoopUnit->compile(); Value *pElOut = builder.CreateGEP(dataOut, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), stateLoop})); builder.CreateStore(elOut, pElOut); //next iteration preparing Value *stateLoopNext = builder.CreateAdd(stateLoop, llvm::ConstantInt::get(tyNum, 1)); stateLoop->addIncoming(stateLoopNext, builder.GetInsertBlock()); //next iteration checks: Value* condAfter = builder.CreateICmpSLE(stateLoopNext, rangeTo); builder.CreateCondBr(condAfter, blockLoop, blockAfterLoop); //finalization: builder.SetInsertPoint(blockAfterLoop); return dataOut; } Value* AdvancedInstructions::compileArrayIndex(llvm::Value* aggregate, std::vector indexes, std::string hintRetVar) { EXPAND_CONTEXT UNUSED(function); UNUSED(scope); indexes.insert(indexes.begin(), llvm::ConstantInt::get(tyNum, 0)); llvm::Value *pEl = llvm->builder.CreateGEP(aggregate, llvm::ArrayRef(indexes)); return llvm->builder.CreateLoad(pEl, NAME("el")); } Value* AdvancedInstructions::compileStructIndex(llvm::Value* aggregate, const ExpandedType& t, const std::string& idx) { EXPAND_CONTEXT UNUSED(scope); UNUSED(function); TypeUtils types(llvm); std::vector&& fields = types.getStructFields(t); for (unsigned i = 0, size = fields.size(); i < size; ++i) { if (fields.at(i) == idx) { //dereference pointer if (types.isPointer(t)) { llvm::Value* addr = llvm->builder.CreateConstGEP2_32(nullptr, aggregate, 0, i); return llvm->builder.CreateLoad(addr); } return llvm->builder.CreateExtractValue(aggregate, llvm::ArrayRef{i}); } } assert(false && "not found required struct field"); return nullptr; } llvm::Value* AdvancedInstructions::compileFold(const Expression& fold, const std::string& hintRetVar) { EXPAND_CONTEXT assert(fold.op == Operator::FOLD); //initialization: Symbol varInSymbol = Attachments::get(fold.getOperands()[0]); Implementation info = Query::queryImplementation(varInSymbol); Iterator* it = Iterator::create(context, varInSymbol); llvm::Value* rangeBegin = it->begin(); llvm::Value* rangeEnd = it->end(); llvm::Value* accumInit = scope->process(fold.getOperands()[1]); std::string varIn = fold.getOperands()[0].getValueString(); std::string varAccum = fold.bindings[1]; std::string varEl = fold.bindings[0]; llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold", function->raw); llvm::BasicBlock *blockLoopBody = llvm::BasicBlock::Create(llvm->llvmContext, "fold_body", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold_after", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "fold_next", function->raw); llvm->builder.CreateBr(blockLoop); // * create phi llvm->builder.SetInsertPoint(blockLoop); llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, varAccum); accum->addIncoming(accumInit, blockBeforeLoop); llvm::PHINode *itLoop = llvm->builder.CreatePHI(rangeBegin->getType(), 2, "foldIt"); itLoop->addIncoming(rangeBegin, blockBeforeLoop); // * loop checks Value* condRange = llvm->builder.CreateICmpNE(itLoop, rangeEnd); llvm->builder.CreateCondBr(condRange, blockLoopBody, blockAfterLoop); // * loop body llvm->builder.SetInsertPoint(blockLoopBody); CodeScope* scopeLoop = fold.blocks.front(); compilation::ICodeScopeUnit* loopUnit = function->getScopeUnit(scopeLoop); Value* elIn = it->get(itLoop); loopUnit->bindArg(accum, move(varAccum)); loopUnit->bindArg(elIn, move(varEl)); Value* accumNext = loopUnit->compile(); // * Loop saturation checks bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); llvm::BasicBlock* blockSaturation = llvm->builder.GetInsertBlock(); if (!flagSaturationTriggered){ llvm->builder.CreateBr(blockNext); } // * computing next iteration state llvm->builder.SetInsertPoint(blockNext); Value *itLoopNext = it->advance(itLoop); accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); itLoop->addIncoming(itLoopNext, llvm->builder.GetInsertBlock()); llvm->builder.CreateBr(blockLoop); // * finalization: llvm->builder.SetInsertPoint(blockAfterLoop); if (!flagSaturationTriggered){ return accum; } llvm::PHINode* result = llvm->builder.CreatePHI(accumInit->getType(), 2); result->addIncoming(accum, blockLoop); result->addIncoming(accumNext, blockSaturation); return result; } llvm::Value* AdvancedInstructions::compileFoldInf(const Expression& fold, const std::string& hintRetVar) { EXPAND_CONTEXT assert(fold.op == Operator::INF); std::string accumName = fold.bindings[0]; llvm::Value* accumInit = scope->process(fold.getOperands()[0]); llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_next", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_post", function->raw); std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); llvm->builder.CreateBr(blockLoop); // * create phi llvm->builder.SetInsertPoint(blockLoop); llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, accumName); accum->addIncoming(accumInit, blockBeforeLoop); // * loop body CodeScope* scopeLoop = fold.blocks.front(); compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); unitLoop->bindArg(accum, move(accumName)); Value* accumNext = unitLoop->compile(); // * Loop saturation checks bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); assert(flagSaturationTriggered); // * computing next iteration state llvm->builder.SetInsertPoint(blockNext); accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); llvm->builder.CreateBr(blockLoop); // finalization: llvm->builder.SetInsertPoint(blockAfterLoop); return accumNext; } llvm::Value* AdvancedInstructions::compileIf(const Expression& exprIf, const std::string& hintRetVar) { EXPAND_CONTEXT const Expression& condExpr = exprIf.getOperands()[0]; llvm::IRBuilder<>& builder = llvm->builder; assert(builder.GetInsertBlock() == scope->currentBlockRaw); //initialization: llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "ifAfter", function->raw); llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm->llvmContext, "ifTrue", function->raw); llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm->llvmContext, "ifFalse", function->raw); llvm::Value* cond = scope->process(condExpr); builder.SetInsertPoint(blockTrue); CodeScope* scopeTrue = exprIf.blocks.front(); llvm::Value* resultTrue = function->getScopeUnit(scopeTrue)->compile(); llvm::BasicBlock * blockTrueEnd = builder.GetInsertBlock(); builder.CreateBr(blockEpilog); builder.SetInsertPoint(blockFalse); CodeScope* scopeFalse = exprIf.blocks.back(); llvm::Value* resultFalse = function->getScopeUnit(scopeFalse)->compile(); llvm::BasicBlock * blockFalseEnd = builder.GetInsertBlock(); builder.CreateBr(blockEpilog); builder.SetInsertPoint(scope->currentBlockRaw); llvm->builder.CreateCondBr(cond, blockTrue, blockFalse); builder.SetInsertPoint(blockEpilog); llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if")); ret->addIncoming(resultTrue, blockTrueEnd); ret->addIncoming(resultFalse, blockFalseEnd); return ret; } //TODO Switch: default variant no needed when all possible conditions are considered llvm::Value* AdvancedInstructions::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(function); AST* root = context.pass->man->root; llvm::IRBuilder<>& builder = llvm->builder; assert(exprSwitch.operands.size() >= 2); assert(exprSwitch.operands[1].op == Operator::CASE_DEFAULT && "No default case in Switch Statement"); int countCases = exprSwitch.operands.size() - 1; llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); builder.SetInsertPoint(blockEpilog); llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch)); llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch")); llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext); builder.SetInsertPoint(blockProlog); llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]); llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm->llvmContext, "caseDefault", function->raw); llvm::SwitchInst * instructionSwitch = builder.CreateSwitch( typeinference::doAutomaticTypeConversion(conditionSwitch, typI8, builder), blockDefault, countCases); for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(i), function->raw); llvm::Value* condCase = function->getScopeUnit(exprSwitch.operands[i].blocks.front())->compile(); builder.SetInsertPoint(blockCase); llvm::Value* resultCase = function->getScopeUnit(exprSwitch.operands[i].blocks.back())->compile(); builder.CreateBr(blockEpilog); ret->addIncoming(resultCase, builder.GetInsertBlock()); builder.SetInsertPoint(blockProlog); instructionSwitch->addCase( dyn_cast( typeinference::doAutomaticTypeConversion(condCase, typI8, builder)), blockCase); } //compile default block: builder.SetInsertPoint(blockDefault); CodeScope* scopeDefault = exprSwitch.operands[1].blocks.front(); llvm::Value* resultDefault = function->getScopeUnit(scopeDefault)->compile(); builder.CreateBr(blockEpilog); ret->addIncoming(resultDefault, builder.GetInsertBlock()); builder.SetInsertPoint(blockEpilog); return ret; } llvm::Value* AdvancedInstructions::compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(function); AST* root = context.pass->man->root; llvm::IRBuilder<>& builder = llvm->builder; llvm::Type* typI8= llvm::Type::getInt8Ty(llvm->llvmContext); const ExpandedType& typVariant = root->getType(exprSwitch.operands.at(0)); llvm::Type* typVariantRaw = llvm->toLLVMType(typVariant); assert(typVariant->__operands.size() == exprSwitch.operands.size() - 1 && "Ill-formed Switch Variant"); int casesCount = exprSwitch.operands.size(); llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); builder.SetInsertPoint(blockEpilog); llvm::Type* resultType = llvm->toLLVMType(root->getType(exprSwitch)); llvm::PHINode *ret = builder.CreatePHI(resultType, casesCount, NAME("switch")); builder.SetInsertPoint(blockProlog); llvm::Value * conditionSwitchRaw = scope->process(exprSwitch.operands.at(0)); llvm::Value* idRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({0})); //Dereference preparation const bool flagPrepareDerefence = std::any_of(typVariant->__operands.begin(), typVariant->__operands.end(), [](const TypeAnnotation& op){ return op.isValid(); }); llvm::Value* addrAsStorage = nullptr; if (flagPrepareDerefence){ assert(exprSwitch.bindings.size() && "Switch condition alias not found"); llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); llvm::Value* storageRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({1})); addrAsStorage = llvm->builder.CreateAlloca(typStorageRaw); llvm->builder.CreateStore(storageRaw, addrAsStorage); } llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(idRaw, nullptr, casesCount); llvm::BasicBlock* blockDefaultUndefined; std::list::const_iterator scopeCaseIt = exprSwitch.blocks.begin(); for (int instancesSize = exprSwitch.operands.size()-1, instId = 0; instId < instancesSize; ++instId) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(instId), function->raw); builder.SetInsertPoint(blockCase); ICodeScopeUnit* unitCase = function->getScopeUnit(*scopeCaseIt); const ExpandedType& instType = ExpandedType(typVariant->__operands.at(instId)); //Actual variant derefence if (instType->isValid()) { string identCondition = exprSwitch.bindings.front(); llvm::Type* instTypeRaw = llvm->toLLVMType(instType); llvm::Value* addrAsInst = llvm->builder.CreateBitOrPointerCast(addrAsStorage, instTypeRaw->getPointerTo()); llvm::Value* instRaw = llvm->builder.CreateLoad(instTypeRaw, addrAsInst); const Symbol& identSymb = unitCase->bindArg(instRaw, move(identCondition)); Attachments::put(identSymb, instType); } llvm::Value* resultCase = function->getScopeUnit(*scopeCaseIt)->compile(); builder.CreateBr(blockEpilog); ret->addIncoming(resultCase, blockDefaultUndefined = builder.GetInsertBlock()); builder.SetInsertPoint(blockProlog); instructionSwitch->addCase(dyn_cast(llvm::ConstantInt::get(typI8, exprSwitch.operands.at(instId+1).getValueDouble())), blockCase); ++scopeCaseIt; } instructionSwitch->setDefaultDest(blockDefaultUndefined); builder.SetInsertPoint(blockEpilog); return ret; } //TODO recognize cases to make const arrays/stored in global mem/stack alloced. llvm::Value* AdvancedInstructions::compileListAsSolidArray(const Expression &expr, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(scope); UNUSED(function); AST* root = context.pass->man->root; const size_t& length = expr.getOperands().size(); const Expression& expression = expr; llvm::Value* zero = ConstantInt::get(tyNum, 0); llvm::Value* one = ConstantInt::get(tyNum, 1); ExpandedType typAggrExpanded = root->getType(expression); assert(typAggrExpanded->__operator == TypeOperator::LIST_ARRAY); llvm::Type* typEl = llvm->toLLVMType(ExpandedType(typAggrExpanded->__operands[0])); ArrayType* typAggr = (ArrayType*) llvm::ArrayType::get(typEl, length); llvm::Value* list = llvm->builder.CreateAlloca(typAggr, ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), length, false), hintRetVar); const std::vector& operands = expression.getOperands(); llvm::Value* addrOperand = llvm->builder.CreateGEP(typAggr, list, ArrayRef(std::vector{zero, zero})); llvm->builder.CreateStore(scope->process(operands.front()), addrOperand) ; for (auto i=++operands.begin(); i!=operands.end(); ++i){ addrOperand = llvm->builder.CreateGEP(typEl, addrOperand, ArrayRef(std::vector{one})); llvm->builder.CreateStore(scope->process(*i), addrOperand) ; } return list; // Value* listDest = l.builder.CreateAlloca(typList, ConstantInt::get(typI32, __size), *hintRetVar); // l.buil1der.CreateMemCpy(listDest, listSource, __size, 16); } llvm::Value* AdvancedInstructions::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(function); UNUSED(scope); Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm->llvmContext)); //ArrayType* typStr = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::I8, size+1)))); /* std::vector chars; chars.reserve(size+1); for (size_t i=0; i< size; ++i){ chars[i] = ConstantInt::get(typI8, (unsigned char) data[i]); } chars[size] = ConstantInt::get(typI8, 0); */ Value* rawData = ConstantDataArray::getString(llvm->llvmContext, data); Value* rawPtrData = llvm->builder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 1, false)); llvm->builder.CreateStore(rawData, rawPtrData); return llvm->builder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar); } llvm::Value* AdvancedInstructions::compileSequence(const Expression &expr){ EXPAND_CONTEXT UNUSED(scope); UNUSED(llvm); llvm::Value* result; for(CodeScope* scope: expr.blocks){ result = function->getScopeUnit(scope)->compile(); } return result; } diff --git a/cpp/src/compilation/advancedinstructions.h b/cpp/src/compilation/advancedinstructions.h index 7b0fbfb..4826c2f 100644 --- a/cpp/src/compilation/advancedinstructions.h +++ b/cpp/src/compilation/advancedinstructions.h @@ -1,52 +1,79 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: AdvancedInstructions.h * Author: pgess * * Created on June 26, 2016, 6:00 PM */ +/** + * \file advancedinstructions.h + * \brief Compound statements compilation + */ + #ifndef INSTRUCTIONSADVANCED_H #define INSTRUCTIONSADVANCED_H #include "ast.h" #include "llvmlayer.h" #include "pass/compilepass.h" #include namespace xreate { namespace compilation { + /** \brief Advanced compilation primitives */ class AdvancedInstructions { public: AdvancedInstructions(compilation::Context ctx); + + /** \brief Array subscript access operator compilation*/ llvm::Value* compileArrayIndex(llvm::Value* aggregate, std::vector indexes, std::string ident = ""); + + /** \brief Struct field access operator compilation*/ llvm::Value* compileStructIndex(llvm::Value* aggregate, const ExpandedType& t, const std::string& idx); /* * - map Computation -> Llvm_Array: Prohibited, we do not know a result size * - map Llvm_Array -> Computation: considered in `compileGetElement` * - map Llvm_Array -> Llvm_Array considered by this method */ + + /** \brief `loop map` statement compilation*/ llvm::Value* compileMapSolidOutput(const Expression &expr, const std::string hintRetVar = ""); + + /** \brief `loop fold` statement compilation*/ llvm::Value* compileFold(const Expression& fold, const std::string& ident=""); + + /** \brief `loop` statement compilation*/ llvm::Value* compileFoldInf(const Expression& fold, const std::string& ident=""); + /** \brief `if` statement compilation*/ llvm::Value* compileIf(const Expression& exprIf, const std::string& ident); + + /** \brief `switch` statement compilation*/ llvm::Value* compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar); + + /** \brief `switch` statement compilation*/ llvm::Value* compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar); + + /** \brief `switch variant` statement compilation*/ llvm::Value* compileConstantStringAsPChar(const std::string &data, const std::string& hintRetVar); + + /** \brief Contiguous memory list implementation*/ llvm::Value* compileListAsSolidArray(const Expression &expr, const std::string& hintRetVar); + + /** \brief `seq` statement compilation */ llvm::Value* compileSequence(const Expression &expr); private: compilation::Context context; llvm::IntegerType* const tyNum; }; }} #endif /* INSTRUCTIONSADVANCED_H */ diff --git a/cpp/src/compilation/containers.cpp b/cpp/src/compilation/containers.cpp index ae85604..eb6c3f5 100644 --- a/cpp/src/compilation/containers.cpp +++ b/cpp/src/compilation/containers.cpp @@ -1,206 +1,208 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: containers.cpp * Author: pgess - * + */ + +/** * \file compilation/containers.h - * \brief Containers compilation support. See more [details on Containers](/w/concepts/containers) + * \brief Containers compilation support. See [Containers](/d/concepts/containers/) in the Xreate's documentation. */ #include "compilation/containers.h" using namespace std; using namespace llvm; using namespace xreate; using namespace xreate::containers; Iterator* Iterator::create(xreate::compilation::Context context, const xreate::Symbol& var){ const Implementation& data = Query::queryImplementation(var); switch(data.impl){ case ON_THE_FLY: return new IteratorForward(context, var, data.extract()); case SOLID: return new IteratorForward(context, var, data.extract()); default: assert(true); } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::begin() { switch(sourceDecl.op) { case xreate::Operator::LIST: { sourceRawType = llvm::Type::getInt32Ty(llvm->llvmContext); return llvm::ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 0); }; case xreate::Operator::LIST_RANGE:{ assert(sourceDecl.operands.size()==2); llvm::Value* result = sourceUnit->process(sourceDecl.operands.at(0)); sourceRawType = result->getType(); return result; }; default: break; } if (linkedlist){ llvm::Value* result = sourceUnit->process(sourceDecl); sourceRawType = result->getType(); return result; } assert(false); } llvm::Value* IteratorForward::end(){ switch(sourceDecl.op) { case xreate::Operator::LIST: { size_t idLast = sourceDecl.operands.size() - 1; return ConstantInt::get(sourceRawType, idLast); } case xreate::Operator::LIST_RANGE: { assert(sourceDecl.operands.size() == 2); llvm::Value* valueEndOfRange = sourceUnit->process(sourceDecl.operands.at(1)); llvm::Value* valueConstOne = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1); return llvm->builder.CreateAdd(valueEndOfRange, valueConstOne); }; default: break; } //return null pointer if (linkedlist){ return ConstantPointerNull::getNullValue(sourceRawType); } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::get(Value* index,const std::string& hintRetVar){ const Expression& currentDecl = CodeScope::getDefinition(current); switch (currentDecl.op) { case xreate::Operator::LIST: { //TODO re check is it right scope(source) to compile currentDecl. Provide unittests. llvm::Value* currentValue = sourceUnit->processSymbol(current); return xreate::compilation::AdvancedInstructions(context).compileArrayIndex(currentValue, std::vector{index}); }; case xreate::Operator::LIST_RANGE: { return index; }; case xreate::Operator::MAP: { assert(currentDecl.getOperands().size()==1); assert(currentDecl.bindings.size()); assert(currentDecl.blocks.size()); CodeScope* scopeLoop = currentDecl.blocks.front(); std::string varEl = currentDecl.bindings[0]; const Symbol& symbIn = Attachments::get(currentDecl.getOperands()[0]); auto it = std::unique_ptr(Iterator::create(context, symbIn)); Value* elIn = it->get(index, varEl); compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); unitLoop->bindArg(elIn, std::move(varEl)); return unitLoop->compile(); } case xreate::Operator::INVALID: { //TODO review iterator determination strategy for case of Expression::BINDING assert(currentDecl.__state==Expression::IDENT); const Symbol& symbIn = Attachments::get(currentDecl); auto it = std::unique_ptr(Iterator::create(context, symbIn)); return it->get(index); }; default: break; } if (linkedlist){ return index; } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::advance(Value* index, const std::string& hintRetVar){ switch(sourceDecl.op) { case xreate::Operator::LIST: case xreate::Operator::LIST_RANGE: return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1), hintRetVar); default: break; } if (linkedlist){ ExpandedType tySource = llvm->ast->getType(CodeScope::getDefinition(source)); assert(tySource->__operator == TypeOperator::LIST_ARRAY && "Linked list implementation has to have ARRAY type"); assert(tySource->__operands.size()); return xreate::compilation::AdvancedInstructions(context).compileStructIndex(index, ExpandedType(TypeAnnotation(tySource->__operands.at(0))), linkedlist.fieldPointer); } assert(false && "Unknown declaration"); return nullptr; } //const ImplementationRec& implementation IteratorForward::IteratorForward(const compilation::Context& ctx, const xreate::Symbol& symbolContainer, const ImplementationRec& implementation) : Iterator(), __length(implementation.size), llvm(ctx.pass->man->llvm) { __container = ctx.function->getScopeUnit(symbolContainer.scope)->processSymbol(symbolContainer); } llvm::Value* IteratorForward::begin(){ //0 return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 0); } llvm::Value* IteratorForward::end(){ //length return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), __length); } llvm::Value* IteratorForward::get(llvm::Value* index,const std::string& hintRetVar){ //GEP[index]] llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm->llvmContext); llvm::Value* pResult = llvm->builder.CreateGEP(__container, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), index})); return llvm->builder.CreateLoad(pResult, hintRetVar); } llvm::Value* IteratorForward::advance(llvm::Value* index, const std::string& hintRetVar){ //index + 1 llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm->llvmContext); return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(tyNum, 1), hintRetVar); } diff --git a/cpp/src/compilation/containers.h b/cpp/src/compilation/containers.h index bd8a4fc..893b387 100644 --- a/cpp/src/compilation/containers.h +++ b/cpp/src/compilation/containers.h @@ -1,96 +1,100 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: containers.h * Author: pgess */ #ifndef CODEINSTRUCTIONS_H #define CODEINSTRUCTIONS_H #include "ast.h" #include "llvmlayer.h" #include "pass/compilepass.h" #include "compilation/advancedinstructions.h" #include "query/containers.h" namespace xreate { namespace containers { using namespace llvm; -/** \brief Factory to create relevant iterator based on solution - * provided by xreate::containers::Query +/** + * \brief A factory to create a concrete iterator based on the solution provided by xreate::containers::Query * \sa xreate::containers::Query */ class Iterator{ public : virtual llvm::Value* begin() =0; virtual llvm::Value* end() = 0; virtual llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") = 0; virtual llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="")=0; virtual ~Iterator(){}; static Iterator* create(xreate::compilation::Context context, const xreate::Symbol& var); }; template class IteratorForward; -/** \brief Possible container implementation. Represents computation on the fly +/** \brief The lazy container implementation. + * + * Represents computation on the fly. * \sa xreate::containers::Iterator, \sa xreate::containers::Query */ template<> class IteratorForward : public Iterator { private: LLVMLayer* llvm; const xreate::Symbol current; const Symbol source; const ImplementationLinkedList linkedlist; const CodeScope* const sourceScope; //TODO initialize and mark as const (three fields) compilation::ICodeScopeUnit* sourceUnit; compilation::IFunctionUnit* function; //TODO is used somewhere? const Expression& sourceDecl; compilation::Context context; llvm::Type* sourceRawType =nullptr; public: IteratorForward(const compilation::Context& ctx, const xreate::Symbol& s, const ImplementationRec& implementation) : llvm(ctx.pass->man->llvm), current(s), source(implementation.source), linkedlist(source), sourceScope(source.scope), sourceUnit(ctx.function->getScopeUnit(source.scope)), sourceDecl(CodeScope::getDefinition(source)), context(ctx) {} llvm::Value* begin() override; llvm::Value* end() override; llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") override; llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="") override; }; -/** \brief Possible container implementation. Represents contiguous in memory(array) implementation +/** \brief The contiguous container implementation. + * + * Represents contiguous in memory(array) implementation. * \sa xreate::containers::Iterator, \sa xreate::containers::Query */ template<> class IteratorForward: public Iterator{ size_t __length; llvm::Value* __container; LLVMLayer* llvm; public: IteratorForward(const compilation::Context& ctx, const xreate::Symbol& symbolContainer, const ImplementationRec& implementation); llvm::Value* begin() override; llvm::Value* end() override; llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") override; llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="") override; }; }} #endif //CODEINSTRUCTIONS_H diff --git a/cpp/src/compilation/interpretation-instructions.cpp b/cpp/src/compilation/interpretation-instructions.cpp index 5ad4ffb..f4387bd 100644 --- a/cpp/src/compilation/interpretation-instructions.cpp +++ b/cpp/src/compilation/interpretation-instructions.cpp @@ -1,116 +1,113 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 15, 2018, 5:32 PM - * */ #include "compilation/interpretation-instructions.h" #include "analysis/interpretation.h" #include "transcendlayer.h" #include "targets.h" #include "aux/latereasoning.h" -#include "latereasoning.h" +#include "latetranscend.h" #include "aux/transcend-decorators.h" #include "compilation/targetinterpretation.h" using namespace std; using namespace xreate::latereasoning; namespace xreate{ namespace interpretation{ Expression IntrinsicQueryInstruction::process(const Expression& expression) { AST* ast = static_cast (__fnI12n->man)->ast; TranscendLayer* transcend = static_cast (__fnI12n->man)->pass->man->transcend; ExpandedType targetT = ast->getType(expression); assert(expression.operands.size() == 1); assert(expression.operands.front().__state == Expression::STRING); assert(targetT->__operator == TypeOperator::LIST_ARRAY); std::string namePredicate = expression.operands.front().getValueString(); StaticModel model = (static_cast (__fnI12n->man))->pass->man->transcend->query(namePredicate); Expression result(Operator::LIST,{}); result.operands.reserve(model.size()); ExpandedType elementT = targetT->__operands.at(0).__operator == TypeOperator::SLAVE ? dereferenceSlaveType(ExpandedType(targetT->__operands.at(0)), transcend) : ExpandedType(targetT->__operands.at(0)); if(model.size()) { if (elementT->__operator == TypeOperator::LIST_RECORD) { //edge case, content's type is LIST_NAMED: for(const auto& row : model) { result.operands.push_back(representTransExpression(row.second, elementT, transcend)); } } else { for (const auto& row : model) { assert(row.second.args().size); result.operands.push_back(representTransExpression(row.second.args()[0], elementT, transcend)); } } } return result; } llvm::Value* IntrinsicQueryInstruction::processLate(const Expression& expression, const compilation::Context& context) { assert(expression.blocks.size()); CodeScope* body = expression.blocks.front(); PassManager* man = static_cast (__fnI12n->man)->pass->man; compilation::ICodeScopeUnit* bodyBrute = context.function->getScopeUnit(body); llvm::Type* instructionT = man->llvm->toLLVMType(man->root->getType(expression)); Expression atomNameE = __fnI12n->getScope(context.scope->scope)->process(expression.operands.front()); assert(atomNameE.__state == Expression::STRING); string atomName = atomNameE.getValueString(); std::string argName = expression.bindings.front(); ExpandedType argT = man->root->getType(body->getDefinition(body->getSymbol(argName))); if (argT->__operator == TypeOperator::SLAVE){ argT = dereferenceSlaveType(argT, man->transcend); } auto transcend = Decorators::getInterface(man->transcend); LateReasoningCompiler* compiler = new latereasoning::LateReasoningCompiler(__fnI12n, context); SymbolPacked targetSP = transcend->pack(Attachments::get(expression)); LateAnnotationsGroup feedbackAG = transcend->queryLate(atomName); for(const auto& feedback : feedbackAG.annotations) { SymbolPacked targetExpectedSP = ParseImplAtom::get(feedback.first); if (targetExpectedSP == targetSP) { const LateAnnotation& annotation = feedback.second; return compiler->compileAutoExpand(annotation, instructionT, "", [transcend, &argT, this, body, bodyBrute, &argName](const Gringo::Symbol & atomRaw) { InterpretationScope* bodyI12n = __fnI12n->getScope(body); Expression argValue; if (argT->__operator == TypeOperator::LIST_RECORD) { argValue = representTransExpression(atomRaw, argT, transcend); } else { argValue = representTransExpression(atomRaw.args()[0], argT, transcend); } bodyI12n->overrideBindings({ {argValue, argName}}); bodyBrute->reset(); return bodyBrute->compile(); }); } } assert(false && "No appropriate late annotation"); return nullptr; //should not be ever reachable } } } //end of xreate::interpretation diff --git a/cpp/src/compilation/interpretation-instructions.h b/cpp/src/compilation/interpretation-instructions.h index 284b094..951fa0e 100644 --- a/cpp/src/compilation/interpretation-instructions.h +++ b/cpp/src/compilation/interpretation-instructions.h @@ -1,46 +1,50 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 15, 2018, 5:28 PM - * + */ + +/** * \file interpretation-instructions.h - * \brief Additional intepretation statements + * \brief The interpretation related statements */ #ifndef INTERPRETATION_INSTRUCTIONS_H #define INTERPRETATION_INSTRUCTIONS_H #include "ast.h" namespace xreate{ namespace compilation{ class Context; } } namespace xreate{ namespace interpretation{ class InterpretationFunction; +/** \brief `intrinsic query` statement */ class IntrinsicQueryInstruction{ public: - IntrinsicQueryInstruction(InterpretationFunction* fn): __fnI12n(fn){ } + + /** \brief Processes `intrinsic query` instruction */ Expression process(const Expression& expression); + + /** \brief Processes `intrinsic query late` instruction */ llvm::Value* processLate(const Expression& expression, const compilation::Context& context); private: InterpretationFunction* __fnI12n; }; } } //end of xreate::interpretation #endif /* INTERPRETATION_INSTRUCTIONS_H */ diff --git a/cpp/src/compilation/latereasoning.cpp b/cpp/src/compilation/latetranscend.cpp similarity index 97% rename from cpp/src/compilation/latereasoning.cpp rename to cpp/src/compilation/latetranscend.cpp index 4b32156..54ce3e7 100644 --- a/cpp/src/compilation/latereasoning.cpp +++ b/cpp/src/compilation/latetranscend.cpp @@ -1,179 +1,183 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * latereasoning.cpp * * Author: pgess * Created on May 26, 2018, 3:54 PM */ -#include "compilation/latereasoning.h" +/** + * \class xreate::latereasoning::LateReasoningCompiler + * Provides Late Transcend feature. See [Late Transcend](/d/transcend/late-transcend/) for the general overview. + */ +#include "compilation/latetranscend.h" //#include "aux/latereasoning.h" #include "compilation/scopedecorators.h" #include "analysis/interpretation.h" #include "compilation/targetinterpretation.h" #include using namespace xreate::interpretation; using namespace xreate::compilation; using namespace std; namespace xreate{ namespace latereasoning{ #define HINT(x) (hint.empty()? x : hint) std::map LateReasoningCompiler:: __dictGuardDefinitions = std::map(); llvm::Value* LateReasoningCompiler::processSwitchLateStatement(const Expression& expr, const std::string& hint) { AST* root = __context.pass->man->root; LLVMLayer* llvm = __context.pass->man->llvm; CodeScope* scopeBody = expr.blocks.front(); Symbol guardS = Symbol{scopeBody->getSymbol(expr.bindings.front()), scopeBody}; const ExpandedType& guardT = root->getType(expr.operands.at(0)); const ExpandedType& guardTPlain = guardT->__operator == TypeOperator::SLAVE? interpretation::dereferenceSlaveType(guardT, __context.pass->man->transcend) : guardT; llvm::Value * guardRaw = __context.scope->process(expr.operands.at(0)); llvm::Type* instructionT = llvm->toLLVMType(root->getType(expr)); return compileExpand(guardS, guardRaw, guardTPlain, instructionT, hint, [this, scopeBody]() { ICodeScopeUnit* bodyUnit = this->__context.function->getScopeUnit(scopeBody); bodyUnit->reset(); return this->__context.function->getScopeUnit(scopeBody)->compile(); }); } llvm::Value* LateReasoningCompiler::compileAutoExpand(const LateAnnotation& annotation, llvm::Type* resultT, const std::string& hint, Handler handler) { TranscendLayer* transcend = __context.pass->man->transcend; AST* root = __context.pass->man->root; const std::list& guardKeys = annotation.guardKeys; std::list guardsToExpand; for(const SymbolPacked key : guardKeys) { if(!__dictGuardDefinitions.count(key)) { const Symbol& keyS = transcend->unpack(key); InterpretationScope* keyScope = __fnI12n->getScope(keyS.scope); if (!keyScope->isBindingDefined(keyS.identifier)) { guardsToExpand.push_back(keyS); } } } typedef std::function < llvm::Value * () > Compiler; Compiler programInit([handler, annotation, this]() { std::list&& values = findKeys(annotation.guardKeys); auto answer = annotation.select(values, __context.pass->man->root, __context.pass->man->transcend); assert(answer); return handler(*answer); }); Compiler aggregate = std::accumulate(guardsToExpand.begin(), guardsToExpand.end(), programInit, [this, root, transcend, &resultT, hint](Compiler program, const Symbol & key) { const ExpandedType& keyT = root->getType(CodeScope::getDefinition(key)); const ExpandedType& keyTPlain = keyT->__operator == TypeOperator::SLAVE? interpretation::dereferenceSlaveType(keyT, transcend) : keyT; return Compiler([this, key, keyTPlain, resultT, hint, program](){ llvm::Value * keyRaw = __context.scope->processSymbol(key); return compileExpand(key, keyRaw, keyTPlain, resultT, hint, program); }); } ); return aggregate(); } llvm::Value* LateReasoningCompiler::compileExpand(const Symbol& keyS, llvm::Value* keyRaw, const ExpandedType& domainT, llvm::Type* resultT, const std::string& hint, CompilerHandler compilerBody) { assert(domainT->__operator == TypeOperator::VARIANT); std::list domInstancesList = generateAllInstancesInDomain2(domainT); std::vector domInstances(domInstancesList.begin(), domInstancesList.end()); const int countInstances = domInstances.size(); assert(countInstances); TranscendLayer* transcend = __context.pass->man->transcend; SymbolPacked keyP = transcend->pack(keyS); LLVMLayer* llvm = __context.pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; compilation::IFunctionUnit* function = __context.function; llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext); llvm::Value* keyVariantRaw = builder.CreateExtractValue(keyRaw, llvm::ArrayRef({0})); llvm::SwitchInst* instructionSwitch = builder.CreateSwitch(keyVariantRaw, nullptr, countInstances); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create( llvm->llvmContext, "epilog", function->raw); builder.SetInsertPoint(blockEpilog); llvm::PHINode *ret = builder.CreatePHI(resultT, countInstances, HINT("reverse")); llvm::BasicBlock* blockDefault = nullptr; for (int instanceId = 0; instanceId < countInstances; ++instanceId) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create( llvm->llvmContext, "case" + std::to_string(instanceId), function->raw); if(instanceId == 0) blockDefault = blockCase; builder.SetInsertPoint(blockCase); //assign guard values const Expression& instanceE = domInstances.at(instanceId); __dictGuardDefinitions[keyP] = instanceE; // __fnI12n->getScope(keyS.scope)->overrideBindings({ // {instanceE, keyS.identifier} // }); //invoke further compilation handler llvm::Value* resultCase = compilerBody(); ret->addIncoming(resultCase, builder.GetInsertBlock()); instructionSwitch->addCase(llvm::dyn_cast(llvm::ConstantInt::get(typI8, instanceId)), blockCase); builder.CreateBr(blockEpilog); } //erase guard assignment __dictGuardDefinitions.erase(keyP); instructionSwitch->setDefaultDest(blockDefault); builder.SetInsertPoint(blockEpilog); return ret; } std::list LateReasoningCompiler::findKeys(const std::list& keys) { TranscendLayer* transcend = __context.pass->man->transcend; std::list result; InterpretationScope* scopeI12n = __fnI12n->getScope(__context.scope->scope); std::transform(keys.begin(), keys.end(), std::inserter(result, result.end()), [this, scopeI12n, transcend](const SymbolPacked & key) { if (__dictGuardDefinitions.count(key)){ return __dictGuardDefinitions.at(key); } return scopeI12n->processSymbol(transcend->unpack(key)); }); return result; } } } diff --git a/cpp/src/compilation/latereasoning.h b/cpp/src/compilation/latetranscend.h similarity index 93% rename from cpp/src/compilation/latereasoning.h rename to cpp/src/compilation/latetranscend.h index 7dc6d59..a2df895 100644 --- a/cpp/src/compilation/latereasoning.h +++ b/cpp/src/compilation/latetranscend.h @@ -1,72 +1,76 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: latereasoning.h * Author: pgess * * Created on May 26, 2018, 3:44 PM */ +/** + * \file latetranscend.h + * \brief Late Transcend support + */ #ifndef CMPLLATEREASONING_H #define CMPLLATEREASONING_H #include "ast.h" #include "aux/latereasoning.h" #include "pass/compilepass.h" #include "llvmlayer.h" #include "transcendlayer.h" #include "targets.h" namespace xreate{ namespace interpretation{ class InterpretationFunction; } } namespace llvm{ class Value; } namespace xreate{ namespace latereasoning{ typedef std::function CompilerHandler; typedef std::function Handler; +/** \brief [Late Transcend](d/transcend/late-transcend/) compilation support */ class LateReasoningCompiler{ public: LateReasoningCompiler(interpretation::InterpretationFunction* fn, compilation::Context ctx) : __context(ctx), __fnI12n(fn){ } llvm::Value* processSwitchLateStatement(const Expression& expr, const std::string& identHint); llvm::Value* compileAutoExpand(const LateAnnotation& annotation, llvm::Type* resultT, const std::string& hint, Handler handler); private: compilation::Context __context; interpretation::InterpretationFunction* __fnI12n; static std::map __dictGuardDefinitions; llvm::Value* compileExpand(const Symbol& keyS, llvm::Value* keyRaw, const ExpandedType& domainT, llvm::Type* resultT, const std::string& hint, CompilerHandler compilerBody); std::list findKeys(const std::list& keys); - }; } } #endif /* CMPLLATEREASONING_H */ diff --git a/cpp/src/compilation/latex.cpp b/cpp/src/compilation/latex.cpp index b853fd8..9a4d11e 100644 --- a/cpp/src/compilation/latex.cpp +++ b/cpp/src/compilation/latex.cpp @@ -1,36 +1,36 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* + * * Author: pgess * Created on June 23, 2018, 4:33 PM - * - * \file latex.cpp - * \brief latex + */ + +/** + * \file src/compilation/latex.h + * \brief Latex(Late Context) support */ #include "compilation/latex.h" #include "analysis/interpretation.h" #include "llvmlayer.h" namespace xreate{ namespace latex{ ExpandedType getSubjectDomain(const std::string& subject, LatexQuery* query){ std::list&& column = query->getSubjectDomain(subject); return ExpandedType(interpretation::collapseColumn(column)); } llvm::Value* ExtraArgsFnInvocation::operator() (std::vector&& args, const std::string& hintDecl) { args.insert(args.end(), __argsLatex.begin(), __argsLatex.end()); return __parent->operator ()(std::move(args), hintDecl); } } } \ No newline at end of file diff --git a/cpp/src/compilation/latex.h b/cpp/src/compilation/latex.h index 339b82e..e35ffeb 100644 --- a/cpp/src/compilation/latex.h +++ b/cpp/src/compilation/latex.h @@ -1,140 +1,144 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on June 23, 2018, 2:51 PM * * \file latex.h * \brief latex */ #ifndef LATEX_H #define LATEX_H -#include "compilation/latereasoning.h" +#include "compilation/latetranscend.h" #include "compilation/interpretation-instructions.h" #include "query/latex.h" #include "pass/compilepass.h" #include "analysis/interpretation.h" #include "compilation/targetinterpretation.h" namespace xreate{ namespace latex{ ExpandedType getSubjectDomain(const std::string& subject, LatexQuery* query); -/** \brief Latex(Late Context)-enabled decorator for IFunctionUnit - * \extends IFunctionUnit +/** \brief Latex(Late Context)-aware decorator for \ref xreate::compilation::IFunctionUnit + * \extends xreate::compilation::IFunctionUnit */ template class LatexBruteFunctionDecorator: public Parent{ public: LatexBruteFunctionDecorator(ManagedFnPtr f, CompilePass* p) : Parent(f, p){ __query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::LatexQuery)); } protected: - std::vector prepareSignature(){ std::vector&& signature = Parent::prepareSignature(); const Demand& demand = __query->getFnDemand(Parent::function->getName()); signature.reserve(signature.size() + demand.size()); int subjectId = __query->LatexParametersOffset; for(const std::string& subject: demand){ const ExpandedType& subjectT = getSubjectDomain(subject, __query); Expression bindingE; bindingE.type = subjectT; std::string argCaption = std::string("latex_") + subject; Parent::function->addBinding( Atom(std::string(argCaption)), std::move(bindingE), subjectId++); llvm::Type* subjectTRaw = Parent::pass->man->llvm->toLLVMType(subjectT); signature.push_back(subjectTRaw); } return signature; } public: LatexQuery* __query; }; +/** \brief %Function invocation operator decorator to handle latex enabled functions with hidden extra arguments */ class ExtraArgsFnInvocation: public compilation::IFnInvocation{ public: ExtraArgsFnInvocation(std::vector argsLatex, compilation::IFnInvocation* parent) : __argsLatex(argsLatex), __parent(parent){ } llvm::Value* operator()(std::vector&& args, const std::string& hintDecl = ""); private: std::vector __argsLatex; compilation::IFnInvocation* __parent; }; +/** + * \brief Latex aware \ref xreate::compilation::ICodeScopeUnit decorator + * \implements xreate::compilation::ICodeScopeUnit + */ template class LatexBruteScopeDecorator: public Parent{ public: LatexBruteScopeDecorator(const CodeScope * const codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass) : Parent(codeScope, f, compilePass){ } compilation::IFnInvocation* findFunction(const Expression& opCall){ compilation::IFnInvocation* invocDefault = Parent::findFunction(opCall); const std::string& calleeName = opCall.getValueString(); LatexQuery* query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::LatexQuery)); const Demand& fnCalleeDemand = query->getFnDemand(calleeName); if(!fnCalleeDemand.size()) return invocDefault; //prepare latex arguments std::vector argsLatex; argsLatex.reserve(fnCalleeDemand.size()); for(const std::string& subject: fnCalleeDemand){ ExpandedType subjectT = getSubjectDomain(subject, query); llvm::Type* subjectTRaw = Parent::pass->man->llvm->toLLVMType(subjectT); const latereasoning::LateAnnotation& decision = query->getDecision(subject, Parent::scope); compilation::Context ctx{this, Parent::function, Parent::pass}; interpretation::InterpretationScope* scopeIntrpr = Parent::pass->targetInterpretation->transformContext(ctx); latereasoning::LateReasoningCompiler* compiler = new latereasoning::LateReasoningCompiler(dynamic_cast(scopeIntrpr->function), ctx); llvm::Value* subjectRaw = compiler->compileAutoExpand( decision, subjectTRaw, subject, [&](const Gringo::Symbol & decisionRaw){ const Expression& decisionE = interpretation::representTransExpression( decisionRaw.args()[2], subjectT, Parent::pass->man->transcend); Attachments::put(decisionE, subjectT); return Parent::process(decisionE, subject); }); argsLatex.push_back(subjectRaw); } return new ExtraArgsFnInvocation(std::move(argsLatex), invocDefault); } }; } } //end of namespace xreate::context #endif /* LATEX_H */ diff --git a/cpp/src/compilation/operators.cpp b/cpp/src/compilation/operators.cpp index 5ad1983..087b766 100644 --- a/cpp/src/compilation/operators.cpp +++ b/cpp/src/compilation/operators.cpp @@ -1,75 +1,71 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * operators.cpp * * Author: pgess * Created on April 8, 2017, 1:35 PM */ -/** - * \file operators.h - * \brief Helpers to compile various operators - */ - #include "operators.h" #include "llvmlayer.h" #include "ExternLayer.h" #include using namespace llvm; using namespace std; namespace xreate { namespace pointerarithmetic { + llvm::Value* PointerArithmetic::add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl){ LLVMLayer* llvm = context.pass->man->llvm; if (left->getType()->isPointerTy() && right->getType()->isIntegerTy()){ std::vector indexes{right}; //{llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0)}; //indexes.push_back(right); return llvm->builder.CreateGEP(left, llvm::ArrayRef(indexes), hintVarDecl); } return nullptr; } }//end of pointgerarithmetic namespace llvm::Value* StructUpdate::add(const Expression& left, llvm::Value *leftRaw, const Expression& right, compilation::Context context, const std::string& hintVarDecl){\ if (!(right.__state == Expression::COMPOUND && right.op == Operator::LIST)){ return nullptr; } PassManager* man = context.pass->man; ExpandedType tyOperandLeft = man->root->getType(left); const std::vector fieldsFormal = (tyOperandLeft.get().__operator == TypeOperator::CUSTOM)? man->llvm->layerExtern->getStructFields(man->llvm->layerExtern->lookupType(tyOperandLeft.get().__valueCustom)) : tyOperandLeft.get().fields; std::map indexFields; for(size_t i=0, size = fieldsFormal.size(); iprocess(value, right.bindings.at(i)); unsigned int fieldId = indexFields.at(right.bindings.at(i)); result = man->llvm->builder.CreateInsertValue(result, valueRaw, llvm::ArrayRef({fieldId})); } return result; } } //end of xreate namespace diff --git a/cpp/src/compilation/operators.h b/cpp/src/compilation/operators.h index d465944..51a0448 100644 --- a/cpp/src/compilation/operators.h +++ b/cpp/src/compilation/operators.h @@ -1,36 +1,46 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: operators.h * Author: pgess * * Created on April 8, 2017, 1:33 PM */ +/** + * \file operators.h + * \brief Helpers to compile various operators + */ + #ifndef OPERATORS_H #define OPERATORS_H #include "pass/compilepass.h" -namespace llvm { - class Value; +namespace llvm{ + class Value; } -namespace xreate { - -namespace pointerarithmetic { - class PointerArithmetic { - public: - static llvm::Value* add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl); - }; -} //end of pointerarithmetic namespace +namespace xreate{ namespace pointerarithmetic{ -class StructUpdate { +/** \brief Pointer arithmetic operators */ +class PointerArithmetic{ public: - static llvm::Value* add(const Expression& left, llvm::Value *leftRaw, const Expression& right, compilation::Context context, const std::string& hintVarDecl); + + /** \brief Pointer arithmetic operator */ + static llvm::Value* add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl); }; +} //end of pointerarithmetic namespace + +/** \brief Struct update operators */ +class StructUpdate{ +public: + + /** \brief Struct update operator */ + static llvm::Value* add(const Expression& left, llvm::Value *leftRaw, const Expression& right, compilation::Context context, const std::string& hintVarDecl); +} ; } //namespace xreate #endif /* OPERATORS_H */ diff --git a/cpp/src/compilation/polymorph.cpp b/cpp/src/compilation/polymorph.cpp index 5d07277..97e654b 100644 --- a/cpp/src/compilation/polymorph.cpp +++ b/cpp/src/compilation/polymorph.cpp @@ -1,53 +1,50 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on July 9, 2018, 6:04 PM - * - * \file polymorph.cpp - * \brief polymorph */ #include "compilation/polymorph.h" using namespace std; namespace xreate{ namespace polymorph{ PolymorphFnInvocation::PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, std::list calleeSpecializations, CompilePass* pass, PolymorphQuery* query, LLVMLayer* llvm, latereasoning::LateReasoningCompiler* compiler) : __selector(selector), __calleeSpecializations(calleeSpecializations), __pass(pass), __query(query), __llvm(llvm), __compiler(compiler) { } llvm::Value* PolymorphFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { std::map dictSelectors; for(ManagedFnPtr specialization : __calleeSpecializations) { dictSelectors.emplace(specialization->guard, specialization); } compilation::IFunctionUnit* specAny = __pass->getFunctionUnit(__calleeSpecializations.front()); return __compiler->compileAutoExpand( __selector, specAny->prepareResult(), hintDecl, [this, &args, hintDecl, &dictSelectors](const Gringo::Symbol & selectorRaw) { const Expression & selectorE = __query->getValue(selectorRaw); assert(dictSelectors.count(selectorE) && "Inappropriate specialization guard"); compilation::BruteFnInvocation* invoc = new compilation::BruteFnInvocation( __pass->getFunctionUnit(dictSelectors.at(selectorE))->compile(), __llvm); return invoc->operator()(move(args), hintDecl); }); } } } diff --git a/cpp/src/compilation/polymorph.h b/cpp/src/compilation/polymorph.h index def1bc1..71194d6 100644 --- a/cpp/src/compilation/polymorph.h +++ b/cpp/src/compilation/polymorph.h @@ -1,96 +1,103 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * File: polymorphcompiler.h * Author: pgess * * Created on October 7, 2017 */ +/** + * \file src/compilation/polymorph.h + * \brief Polymorphism-aware compilation routines + */ + #ifndef POLYMORPHCOMPILER_H #define POLYMORPHCOMPILER_H #include "pass/compilepass.h" #include "query/polymorph.h" -#include "compilation/latereasoning.h" +#include "compilation/latetranscend.h" #include "compilation/targetinterpretation.h" namespace xreate{ namespace polymorph{ typedef Expression Selector; +/** \brief An instance of \ref compilation::IFnInvocation to manage polymorphic functions invocation*/ class PolymorphFnInvocation: public compilation::IFnInvocation{ public: - PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, - std::list calleeSpecializations, - CompilePass* pass, - PolymorphQuery* query, - LLVMLayer* llvm, - latereasoning::LateReasoningCompiler* compiler); + PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, + std::list calleeSpecializations, + CompilePass* pass, + PolymorphQuery* query, + LLVMLayer* llvm, + latereasoning::LateReasoningCompiler* compiler); - llvm::Value* operator()(std::vector&& args, const std::string& hintDecl = ""); + llvm::Value* operator()(std::vector&& args, const std::string& hintDecl = ""); private: - latereasoning::LateAnnotation __selector; - std::list __calleeSpecializations; - - CompilePass* __pass; - PolymorphQuery* __query; - LLVMLayer* __llvm; - latereasoning::LateReasoningCompiler* __compiler; -}; - + latereasoning::LateAnnotation __selector; + std::list __calleeSpecializations; + + CompilePass* __pass; + PolymorphQuery* __query; + LLVMLayer* __llvm; + latereasoning::LateReasoningCompiler* __compiler; +} ; + +/** + * \brief Polymorphism aware \ref xreate::compilation::ICodeScopeUnit decorator + * \implements xreate::compilation::ICodeScopeUnit + */ template class PolymorphCodeScopeUnit: public Parent{ public: - - PolymorphCodeScopeUnit(const CodeScope * const codeScope, - compilation::IFunctionUnit* f, - CompilePass* compilePass) - : Parent(codeScope, f, compilePass){ } + PolymorphCodeScopeUnit(const CodeScope * const codeScope, + compilation::IFunctionUnit* f, + CompilePass* compilePass) + : Parent(codeScope, f, compilePass){ } protected: + compilation::IFnInvocation* + findFunction(const Expression& opCall) override{ + // //Check does invocation require guards satisfaction + const std::string& nameCallee = opCall.getValueString(); + const std::list& specializations = + Parent::pass->man->root->getFunctionSpecializations(nameCallee); + + //Extern function + if(specializations.size() == 0){ + return Parent::findFunction(opCall); + } - compilation::IFnInvocation* - findFunction(const Expression& opCall) override{ -// //Check does invocation require guards satisfaction - const std::string& nameCallee = opCall.getValueString(); - const std::list& specializations = - Parent::pass->man->root->getFunctionSpecializations(nameCallee); - - //Extern function - if(specializations.size() == 0){ - return Parent::findFunction(opCall); - } - - //No other specializations. Check if it has no guard - if(specializations.size() == 1){ - if(!specializations.front()->guard.isValid()){ - return Parent::findFunction(opCall); - } - } - - //Several specializations - PolymorphQuery* query = dynamic_cast ( - Parent::pass->man->transcend->getQuery(QueryId::PolymorphQuery)); - const latereasoning::LateAnnotation& selector = query->get(opCall); - - compilation::Context ctx{this, Parent::function, Parent::pass}; - interpretation::InterpretationScope* scopeIntrpr = - Parent::pass->targetInterpretation->transformContext(ctx); - latereasoning::LateReasoningCompiler* compiler - = new latereasoning::LateReasoningCompiler(dynamic_cast(scopeIntrpr->function), ctx); - - return new PolymorphFnInvocation(selector, specializations, Parent::pass, - query, Parent::pass->man->llvm, compiler); + //No other specializations. Check if it has no guard + if(specializations.size() == 1){ + if(!specializations.front()->guard.isValid()){ + return Parent::findFunction(opCall); + } } -}; + + //Several specializations + PolymorphQuery* query = dynamic_cast ( + Parent::pass->man->transcend->getQuery(QueryId::PolymorphQuery)); + const latereasoning::LateAnnotation& selector = query->get(opCall); + + compilation::Context ctx{this, Parent::function, Parent::pass}; + interpretation::InterpretationScope* scopeIntrpr = + Parent::pass->targetInterpretation->transformContext(ctx); + latereasoning::LateReasoningCompiler* compiler + = new latereasoning::LateReasoningCompiler(dynamic_cast (scopeIntrpr->function), ctx); + + return new PolymorphFnInvocation(selector, specializations, Parent::pass, + query, Parent::pass->man->llvm, compiler); + } +} ; } } //end of xreate::polymorph #endif /* POLYMORPHCOMPILER_H */ diff --git a/cpp/src/compilation/scopedecorators.h b/cpp/src/compilation/scopedecorators.h index cf26986..b84b9ae 100644 --- a/cpp/src/compilation/scopedecorators.h +++ b/cpp/src/compilation/scopedecorators.h @@ -1,183 +1,188 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: scopedecorators.h * Author: pgess * * Created on February 24, 2017, 11:35 AM */ /** * \file scopedecorators.h * \brief Basic code block compilation xreate::compilation::ICodeScopeUnit decorators */ #ifndef SCOPEDECORATORS_H #define SCOPEDECORATORS_H #include "ast.h" #include "compilation/targetinterpretation.h" #include "compilation/versions.h" #include "compilation/transformations.h" #include "compilation/polymorph.h" #include "compilation/latex.h" #include "analysis/typeinference.h" #include namespace xreate { class CompilePass; namespace compilation { class ICodeScopeUnit; class IFunctionUnit; -/**\brief Caching ability for code scope compilation +/**\brief Provides caching ability for code scope compilation * \extends xreate::compilation::ICodeScopeUnit */ template class CachedScopeDecorator: public Parent{ typedef CachedScopeDecorator SELF; public: CachedScopeDecorator(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} Symbol bindArg(llvm::Value* value, std::string&& alias) { //ensure existence of an alias assert(Parent::scope->__identifiers.count(alias)); //memorize new value for an alias ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE}; __rawVars[id] = value; return Symbol{id, Parent::scope}; } void bindArg(llvm::Value* value, const ScopedSymbol& s) { __rawVars[s] = value; } llvm::Value* compile(const std::string& hintBlockDecl="") override{ if (__rawVars.count(ScopedSymbol::RetSymbol)){ return __rawVars[ScopedSymbol::RetSymbol]; } return Parent::compile(hintBlockDecl); } llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar) override{ const CodeScope* scope = s.scope; SELF* self = dynamic_cast(Parent::function->getScopeUnit(scope)); if (self->__rawVars.count(s.identifier)){ return self->__rawVars[s.identifier]; } //Declaration could be overriden /* Expression declaration = CodeScope::getDefinition(s, true); if (!declaration.isDefined()){ assert(__declarationsOverriden.count(s.identifier)); declaration = __declarationsOverriden[s.identifier]; } else { (false); //in case of binding there should be raws provided. } } */ llvm::Value* resultRaw = Parent::processSymbol(s, hintRetVar); self->__rawVars.emplace(s.identifier, resultRaw); return resultRaw; } void overrideDeclarations(std::list> bindings){ reset(); for (auto entry: bindings){ SELF* self = dynamic_cast(Parent::function->getScopeUnit(entry.first.scope)); assert(self == this); self->__declarationsOverriden.emplace(entry.first.identifier, entry.second); } } void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __rawVars.clear(); __declarationsOverriden.clear(); __childScopes.clear(); } private: std::unordered_map __declarationsOverriden; std::unordered_map __rawVars; std::list> __childScopes; }; +/** \brief Provides automatic type conversion + * \extends xreate::compilation::ICodeScopeUnit + */ template class TypeConversionScopeDecorator: public Parent { public: TypeConversionScopeDecorator(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="") override { llvm::Value* resultR = Parent::process(expr, hintVarDecl); if(!expr.type.isValid()) { return resultR; } ExpandedType exprT = Parent::pass->man->root->getType(expr); llvm::Type* exprTR = Parent::pass->man->llvm->toLLVMType(exprT); return typeinference::doAutomaticTypeConversion(resultR, exprTR, Parent::pass->man->llvm->builder); } }; -/**\brief Default code scope compilation functionality*/ +/**\brief The default code scope compilation implementation + * \extends xreate::compilation::ICodeScopeUnit + */ typedef CachedScopeDecorator< TypeConversionScopeDecorator< latex::LatexBruteScopeDecorator< polymorph::PolymorphCodeScopeUnit< compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< versions::VersionsScopeDecorator>>>>>> DefaultCodeScopeUnit; } //end of compilation namespace struct CachedScopeDecoratorTag; struct VersionsScopeDecoratorTag; template<> struct DecoratorsDict{ typedef compilation::CachedScopeDecorator< compilation::TypeConversionScopeDecorator< latex::LatexBruteScopeDecorator< polymorph::PolymorphCodeScopeUnit< compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< versions::VersionsScopeDecorator>>>>>> result; }; template<> struct DecoratorsDict{ typedef versions::VersionsScopeDecorator< compilation::BasicCodeScopeUnit> result; }; } //end of xreate #endif /* SCOPEDECORATORS_H */ diff --git a/cpp/src/compilation/targetinterpretation.cpp b/cpp/src/compilation/targetinterpretation.cpp index 4f4555a..f22ebc1 100644 --- a/cpp/src/compilation/targetinterpretation.cpp +++ b/cpp/src/compilation/targetinterpretation.cpp @@ -1,627 +1,626 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: targetinterpretation.cpp * Author: pgess * * Created on June 29, 2016, 6:45 PM */ /** * \file targetinterpretation.h - * \brief Interpretation support. See more [details on Interpretation](/w/concepts/dsl/) + * \brief Interpretation support. See more details on [Interpretation](/d/concepts/interpretation/) */ #include "compilation/targetinterpretation.h" #include "pass/interpretationpass.h" #include "analysis/typeinference.h" #include "llvmlayer.h" #include "compilation/scopedecorators.h" #include "compilation/interpretation-instructions.h" #include #include #include using namespace std; using namespace xreate::compilation; namespace xreate{ namespace interpretation{ const Expression EXPRESSION_FALSE = Expression(Atom(0)); const Expression EXPRESSION_TRUE = Expression(Atom(1)); CodeScope* InterpretationScope::processOperatorIf(const Expression& expression) { const Expression& exprCondition = process(expression.getOperands()[0]); if (exprCondition == EXPRESSION_TRUE) { return expression.blocks.front(); } return expression.blocks.back(); } CodeScope* InterpretationScope::processOperatorSwitch(const Expression& expression) { const Expression& exprCondition = process(expression.operands[0]); bool flagHasDefault = expression.operands[1].op == Operator::CASE_DEFAULT; //TODO check that one and only one case variant is appropriate for (size_t size = expression.operands.size(), i = flagHasDefault ? 2 : 1; i < size; ++i) { const Expression& exprCase = process(expression.operands[i]); if (function->getScope((const CodeScope*) exprCase.blocks.front())->processScope() == exprCondition) { return exprCase.blocks.back(); } } if (flagHasDefault) { const Expression& exprCaseDefault = expression.operands[1]; return exprCaseDefault.blocks.front(); } assert(false && "Switch has no appropriate variant"); return nullptr; } CodeScope* InterpretationScope::processOperatorSwitchVariant(const Expression& expression) { const Expression& condition = process(expression.operands.at(0)); assert(condition.op == Operator::VARIANT); const string& identCondition = expression.bindings.front(); Expression opExpected(Atom(condition.getValueDouble())); auto itFoundValue = std::find(++expression.operands.begin(), expression.operands.end(), opExpected); assert(itFoundValue != expression.operands.end()); int indexBlock = itFoundValue - expression.operands.begin() - 1; auto blockFound = expression.blocks.begin(); std::advance(blockFound, indexBlock); InterpretationScope* scopeI12n = function->getScope(*blockFound); if(condition.operands.size()) { const Expression& value = condition.operands.at(0); scopeI12n->overrideBindings({ {value, identCondition} }); } return *blockFound; } llvm::Value* InterpretationScope::processLate(const InterpretationOperator& op, const Expression& expression, const Context& context) { switch(op) { case IF_INTERPRET_CONDITION: { CodeScope* scopeResult = processOperatorIf(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_INTERPRET_CONDITION: { CodeScope* scopeResult = processOperatorSwitch(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); const Expression& condition = expression.operands.at(0); const Expression& valueCondition = process(condition); const string identCondition = expression.bindings.front(); auto scopeCompilation = Decorators::getInterface(context.function->getScopeUnit(scopeResult)); if(valueCondition.operands.size()) { //override value Symbol symbCondition{ScopedSymbol{scopeResult->__identifiers.at(identCondition), versions::VERSION_NONE}, scopeResult}; scopeCompilation->overrideDeclarations({ {symbCondition, Expression(valueCondition.operands.at(0))}} ); //set correct type for binding: TypeAnnotation typeVariant = typeinference::getType(condition, *function->man->ast); int conditionIndex = valueCondition.getValueDouble(); ScopedSymbol symbolInternal = scopeResult->getSymbol(identCondition); scopeResult->__declarations[symbolInternal].bindType(typeVariant.__operands.at(conditionIndex)); } llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_LATE: { latereasoning::LateReasoningCompiler compiler(dynamic_cast(this->function), context); return compiler.processSwitchLateStatement(expression, ""); } case FOLD_INTERPRET_INPUT: { //initialization const Expression& exprInput = process(expression.getOperands()[0]); assert(exprInput.op == Operator::LIST); CodeScope* scopeBody = expression.blocks.front(); const string& nameEl = expression.bindings[0]; Symbol symbEl{ScopedSymbol{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody}; const std::string& idAccum = expression.bindings[1]; llvm::Value* rawAccum = context.scope->process(expression.getOperands()[1]); InterpretationScope* intrBody = function->getScope(scopeBody); auto unitBody = Decorators::getInterface(context.function->getScopeUnit(scopeBody)); const std::vector elementsInput = exprInput.getOperands(); for(size_t i = 0; i < elementsInput.size(); ++i) { const Expression& exprElement = elementsInput[i]; intrBody->overrideBindings({ {exprElement, nameEl} }); unitBody->overrideDeclarations({ {symbEl, exprElement} }); //resets unitBody unitBody->bindArg(rawAccum, string(idAccum)); rawAccum = unitBody->compile(); } return rawAccum; } // case FOLD_INF_INTERPRET_INOUT: // { // } //TODO refactor as InterpretationCallStatement class case CALL_INTERPRET_PARTIAL: { const std::string &calleeName = expression.getValueString(); ICodeScopeUnit* scopeUnitSelf = context.scope; ManagedFnPtr callee = this->function->man->ast->findFunction(calleeName); const FunctionInterpretationData& calleeData = FunctionInterpretationHelper::getSignature(callee); std::vector argsActual; PIFSignature sig; sig.declaration = callee; for(size_t no = 0, size = expression.operands.size(); no < size; ++no) { const Expression& op = expression.operands[no]; if (calleeData.signature.at(no) == INTR_ONLY) { sig.bindings.push_back(process(op)); continue; } argsActual.push_back(scopeUnitSelf->process(op)); } TargetInterpretation* man = dynamic_cast (this->function->man); PIFunction* pifunction = man->getFunction(move(sig)); llvm::Function* raw = pifunction->compile(); boost::scoped_ptr statement(new BruteFnInvocation(raw, man->pass->man->llvm)); return (*statement)(move(argsActual)); } case QUERY_LATE: { return IntrinsicQueryInstruction( dynamic_cast(this->function)) .processLate(expression, context); } default: break; } assert(false && "Unknown late interpretation operator"); return nullptr; } llvm::Value* InterpretationScope::compile(const Expression& expression, const Context& context) { const InterpretationData& data = Attachments::get(expression); if (data.op != InterpretationOperator::NONE) { return processLate(data.op, expression, context); } Expression result = process(expression); return context.scope->process(result); } Expression InterpretationScope::process(const Expression& expression) { #ifndef NDEBUG if (expression.tags.count("bpoint")) { std::raise(SIGINT); } #endif PassManager* man = (static_cast (function->man))->pass->man; switch (expression.__state) { case Expression::INVALID: assert(false); case Expression::NUMBER: case Expression::STRING: return expression; case Expression::IDENT: { Symbol s = Attachments::get(expression); return Parent::processSymbol(s); } case Expression::COMPOUND: break; default: assert(false); } switch (expression.op) { case Operator::EQU: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_TRUE; return EXPRESSION_FALSE; } case Operator::NE: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_FALSE; return EXPRESSION_TRUE; } case Operator::LOGIC_AND: { assert(expression.operands.size() == 1); return process (expression.operands[0]); } // case Operator::LOGIC_OR: case Operator::CALL: { const std::string &fnName = expression.getValueString(); ManagedFnPtr fnAst = this->function->man->ast->findFunction(fnName); InterpretationFunction* fnUnit = this->function->man->getFunction(fnAst); vector args; args.reserve(expression.getOperands().size()); for(size_t i = 0, size = expression.getOperands().size(); i < size; ++i) { args.push_back(process(expression.getOperands()[i])); } return fnUnit->process(args); } case Operator::CALL_INTRINSIC: { assert(false && "Unknown intrinsic"); } case Operator::QUERY: { return IntrinsicQueryInstruction(dynamic_cast(this->function)) .process(expression); } case Operator::QUERY_LATE: { assert(false && "Can't be interpretated"); return Expression(); } case Operator::IF: { CodeScope* scopeResult = processOperatorIf(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH: { CodeScope* scopeResult = processOperatorSwitch(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); return function->getScope(scopeResult)->processScope(); } case Operator::VARIANT: { if(!expression.operands.size()) return expression; Expression variantData = process(expression.operands[0]); Expression result{Operator::VARIANT, {variantData}}; result.setValueDouble(expression.getValueDouble()); return result; } case Operator::INDEX: { Expression exprData = process(expression.operands[0]); for (size_t keyId = 1; keyId < expression.operands.size(); ++keyId) { const Expression& exprKey = process(expression.operands[keyId]); if (exprKey.__state == Expression::STRING) { const string& key = exprKey.getValueString(); assert(exprData.__indexBindings.count(key)); size_t idxKey = exprData.__indexBindings.at(key); exprData = Expression(exprData.operands.at(idxKey)); continue; } if (exprKey.__state == Expression::NUMBER) { int key = exprKey.getValueDouble(); exprData = Expression(exprData.operands[key]); continue; } assert(false && "Inappropriate key"); } return exprData; } case Operator::FOLD: { const Expression& exprInput = process(expression.getOperands()[0]); const Expression& exprInit = process(expression.getOperands()[1]); const std::string& argEl = expression.bindings[0]; const std::string& argAccum = expression.bindings[1]; InterpretationScope* body = function->getScope(expression.blocks.front()); Expression accum = exprInit; for(size_t size = exprInput.getOperands().size(), i = 0; i < size; ++i) { body->overrideBindings({ {exprInput.getOperands()[i], argEl}, {accum, argAccum} }); accum = body->processScope(); } return accum; } case Operator::LIST: case Operator::LIST_RANGE: { Expression result(expression.op,{}); result.operands.resize(expression.operands.size()); result.bindings = expression.bindings; result.__indexBindings = expression.__indexBindings; int keyId = 0; for(const Expression& opCurrent : expression.operands) { result.operands[keyId++] = process(opCurrent); } return result; } // case Operator::MAP: { // break; // } default: break; } return expression; } InterpretationFunction* TargetInterpretation::getFunction(IFunctionUnit* unit) { if (__dictFunctionsByUnit.count(unit)) { return __dictFunctionsByUnit.at(unit); } InterpretationFunction* f = new InterpretationFunction(unit->function, this); __dictFunctionsByUnit.emplace(unit, f); assert(__functions.emplace(unit->function.id(), f).second); return f; } PIFunction* TargetInterpretation::getFunction(PIFSignature&& sig) { auto f = __pifunctions.find(sig); if (f != __pifunctions.end()) { return f->second; } PIFunction* result = new PIFunction(PIFSignature(sig), __pifunctions.size(), this); __pifunctions.emplace(move(sig), result); assert(__dictFunctionsByUnit.emplace(result->functionUnit, result).second); return result; } InterpretationScope* TargetInterpretation::transformContext(const Context& c) { return this->getFunction(c.function)->getScope(c.scope->scope); } llvm::Value* TargetInterpretation::compile(const Expression& expression, const Context& ctx) { return transformContext(ctx)->compile(expression, ctx); } InterpretationFunction::InterpretationFunction(const ManagedFnPtr& function, Target* target) : Function(function, target) { } Expression InterpretationFunction::process(const std::vector& args) { InterpretationScope* body = getScope(__function->__entry); list> bindings; for(size_t i = 0, size = args.size(); i < size; ++i) { bindings.push_back(make_pair(args.at(i), body->scope->__bindings.at(i))); } body->overrideBindings(bindings); return body->processScope(); } // Partial function interpretation typedef BasicFunctionUnit PIFunctionUnitParent; class PIFunctionUnit : public PIFunctionUnitParent{ public: PIFunctionUnit(ManagedFnPtr f, std::set&& arguments, size_t id, CompilePass* p) : PIFunctionUnitParent(f, p), argumentsActual(move(arguments)), __id(id) { } protected: std::vector prepareSignature() override { LLVMLayer* llvm = PIFunctionUnitParent::pass->man->llvm; AST* ast = PIFunctionUnitParent::pass->man->root; CodeScope* entry = PIFunctionUnitParent::function->__entry; std::vector signature; for(size_t no : argumentsActual) { VNameId argId = entry->__identifiers.at(entry->__bindings.at(no)); ScopedSymbol arg{argId, versions::VERSION_NONE}; signature.push_back(llvm->toLLVMType(ast->expandType(entry->__declarations.at(arg).type))); } return signature; } llvm::Function::arg_iterator prepareBindings() override{ CodeScope* entry = PIFunctionUnitParent::function->__entry; ICodeScopeUnit* entryCompilation = PIFunctionUnitParent::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = PIFunctionUnitParent::raw->arg_begin(); for(size_t no : argumentsActual) { ScopedSymbol arg{entry->__identifiers.at(entry->__bindings.at(no)), versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, arg); fargsI->setName(entry->__bindings.at(no)); ++fargsI; } return fargsI; } virtual std::string prepareName() override { return PIFunctionUnitParent::prepareName() + "_" + std::to_string(__id); } private: std::set argumentsActual; size_t __id; } ; PIFunction::PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target) : InterpretationFunction(sig.declaration, target), signatureInstance(move(sig)) { const FunctionInterpretationData& functionData = FunctionInterpretationHelper::getSignature(signatureInstance.declaration); std::set argumentsActual; for (size_t no = 0, size = functionData.signature.size(); no < size; ++no) { if (functionData.signature.at(no) != INTR_ONLY) { argumentsActual.insert(no); } } functionUnit = new PIFunctionUnit(signatureInstance.declaration, move(argumentsActual), id, target->pass); CodeScope* entry = signatureInstance.declaration->__entry; auto entryUnit = Decorators::getInterface<>(functionUnit->getEntry()); InterpretationScope* entryIntrp = InterpretationFunction::getScope(entry); list> bindingsPartial; list> declsPartial; for(size_t no = 0, sigNo = 0, size = entry->__bindings.size(); no < size; ++no) { if(functionData.signature.at(no) == INTR_ONLY) { bindingsPartial.push_back({signatureInstance.bindings[sigNo], entry->__bindings[no]}); VNameId argId = entry->__identifiers.at(entry->__bindings[no]); Symbol argSymbol{ScopedSymbol {argId, versions::VERSION_NONE}, entry}; declsPartial.push_back({argSymbol, signatureInstance.bindings[sigNo]}); ++sigNo; } } entryIntrp->overrideBindings(bindingsPartial); entryUnit->overrideDeclarations(declsPartial); } llvm::Function* PIFunction::compile() { llvm::Function* raw = functionUnit->compile(); return raw; } bool operator<(const PIFSignature& lhs, const PIFSignature& rhs) { if (lhs.declaration.id() != rhs.declaration.id()) { return lhs.declaration.id() < rhs.declaration.id(); } return lhs.bindings < rhs.bindings; } bool operator<(const PIFSignature& lhs, PIFunction * const rhs) { return lhs < rhs->signatureInstance; } bool operator<(PIFunction * const lhs, const PIFSignature& rhs) { return lhs->signatureInstance < rhs; } } } /** \class xreate::interpretation::InterpretationFunction * * Holds list of xreate::interpretation::InterpretationScope 's focused on interpretation of individual code scopes * - * There is particulat subclass PIFunction intended to represent partially interpreted functions + * There is particulat subclass PIFunction intended to represent partially interpreted functions. *\sa TargetInterpretation, [Interpretation Concept](/w/concepts/dfa) */ + /** \class xreate::interpretation::TargetInterpretation * - * Executed during compilation and intented to preprocess eligible parts of code. - * Established on [Targets Infrastructure](\ref compilation::Target) + * TargetInterpretation is executed during compilation and is intended to preprocess eligible for interpretation parts of a source code. * - * Holds list of InterpretationFunction / PIFunction to represent interpretation process for individual functions + * Keeps a list of InterpretationFunction / PIFunction that represent interpretation for an individual functions. * - * In order to be activated during compilation process there is - * InterpretationScopeDecorator implementation of ICodeScopeUnit - * \sa InterpretationPass, compilation::Target, [Interpretation Concept](/w/concepts/dfa) + * There is \ref InterpretationScopeDecorator that embeds interpretation to an overall compilation process. + * \sa InterpretationPass, compilation::Target, [Interpretation Concept](/d/concepts/interpretation/) * */ diff --git a/cpp/src/compilation/targetinterpretation.h b/cpp/src/compilation/targetinterpretation.h index 867b2f4..8ff000b 100644 --- a/cpp/src/compilation/targetinterpretation.h +++ b/cpp/src/compilation/targetinterpretation.h @@ -1,130 +1,133 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: targetstatic.h * Author: pgess * * Created on July 2, 2016, 1:25 PM */ #ifndef TARGETSTATIC_H #define TARGETSTATIC_H #include "ast.h" #include "pass/compilepass.h" #include "compilation/targets.h" #include "pass/interpretationpass.h" #include "transcendlayer.h" namespace xreate{ namespace interpretation{ class TargetInterpretation; class InterpretationScope; class InterpretationFunction; }} namespace xreate{ namespace compilation{ template <> struct TargetInfo { typedef Expression Result; typedef interpretation::InterpretationScope Scope; typedef interpretation::InterpretationFunction Function; }; }} namespace xreate{ namespace interpretation{ - /** \brief Encapsulates interpretation of a single Code Scope */ +/** \brief Encapsulates interpretation of a single code scope */ class InterpretationScope: public compilation::Scope{ typedef Scope Parent; public: InterpretationScope(const CodeScope* scope, compilation::Function* f): Parent(scope, f) {} Expression process(const Expression& expression) override; llvm::Value* compile(const Expression& expression, const compilation::Context& context); private: llvm::Value* processLate(const InterpretationOperator& op, const Expression& expression, const compilation::Context& context); //llvm::Value* compilePartialFnCall(const Expression& expression, const Context& context); CodeScope* processOperatorIf(const Expression& expression); CodeScope* processOperatorSwitch(const Expression& expression); CodeScope* processOperatorSwitchVariant(const Expression& expression); }; -/** \brief Encapsulates interpretation of a single %Function */ +/** \brief Encapsulates interpretation of a single function */ class InterpretationFunction: public compilation::Function{ public: InterpretationFunction(const ManagedFnPtr& function, compilation::Target* target); Expression process(const std::vector& args); }; /** \brief Signature of a partially interpreted function */ struct PIFSignature{ ManagedFnPtr declaration; std::vector bindings; }; class PIFunctionUnit; -/** \brief Partially interpreted function */ +/** \brief Encapsulates partially interpreted function */ class PIFunction: public InterpretationFunction{ public: PIFunctionUnit* functionUnit; PIFSignature signatureInstance; PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target); llvm::Function* compile(); }; bool operator<(const PIFSignature& lhs, PIFunction* const rhs); bool operator<(PIFunction* const lhs, const PIFSignature& rhs); -/** \brief Encapsulates actual [Interpretation](/w/concepts/dfa) based on InterpretationPass analysis results */ +/** \brief Encapsulates interpretation process based on analysis results from \ref InterpretationPass + * \extends compilation::Target + * \sa InterpretationPass + */ class TargetInterpretation: public compilation::Target{ public: TargetInterpretation(AST* root, CompilePass* passCompilation): Target(root), pass(passCompilation){} //target: public: InterpretationFunction* getFunction(compilation::IFunctionUnit* unit); PIFunction* getFunction(PIFSignature&& sig); private: std::map __pifunctions; std::map __dictFunctionsByUnit; //self: public: CompilePass* pass; llvm::Value* compile(const Expression& expression, const compilation::Context& ctx); InterpretationScope* transformContext(const compilation::Context& c); private: }; /**\brief Interpretation-aware Code Scope decorator * \extends xreate::compilation::ICodeScopeUnit */ template class InterpretationScopeDecorator: public Parent{ public: InterpretationScopeDecorator(const CodeScope* const codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl){ const InterpretationData& data = Attachments::get(expr, {ANY, NONE}); bool flagInterpretationEligible = (data.resolution == INTR_ONLY || data.op != InterpretationOperator::NONE); if (flagInterpretationEligible){ compilation::Context ctx{this, this->function, this->pass}; return Parent::pass->targetInterpretation->compile(expr, ctx); } return Parent::process(expr, hintVarDecl); } }; }} //end of xreate:: interpretation #endif /* TARGETSTATIC_H */ \ No newline at end of file diff --git a/cpp/src/compilation/transformersaturation.h b/cpp/src/compilation/transformersaturation.h index bb370a1..478956b 100644 --- a/cpp/src/compilation/transformersaturation.h +++ b/cpp/src/compilation/transformersaturation.h @@ -1,45 +1,46 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: transformersaturation.h * Author: pgess * * Created on March 25, 2017, 9:59 PM */ #ifndef TRANSFORMERSATURATION_H #define TRANSFORMERSATURATION_H #include "transformations.h" namespace xreate { namespace compilation { +/** \brief Loop saturation support */ class TransformerSaturation: public Transformer{ public: TransformerSaturation(llvm::BasicBlock* allocationBlock, TransformationsManager* manager); ~TransformerSaturation(); llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx) override; void processBreak(const Context& ctx); void allocateFlag(const Context& ctx); bool insertSaturationChecks(llvm::BasicBlock* blockContinue, llvm::BasicBlock* blockExit, const Context& ctx); private: TransformationsManager* man; TransformerSaturation* oldInstance = nullptr; llvm::BasicBlock* blockAllocation; llvm::Value* flagSaturation = nullptr; }; template <> struct TransformerInfo { static const unsigned int id = 0; }; } } #endif /* TRANSFORMERSATURATION_H */ diff --git a/cpp/src/compilation/versions.h b/cpp/src/compilation/versions.h index 3aeaab5..a45dcbe 100644 --- a/cpp/src/compilation/versions.h +++ b/cpp/src/compilation/versions.h @@ -1,155 +1,124 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * versions.cpp * * Author: pgess * Created on January 21, 2017, 1:24 PM */ /** - * \file - * \brief CodeScope's Decorator to support Versions + * \file versions.h + * \brief Versions-aware compilation support */ #include "pass/versionspass.h" #include "pass/compilepass.h" #include "llvmlayer.h" namespace xreate { class CompilePass; namespace compilation { class ICodeScopeUnit; class IFunctionUnit; } namespace versions{ -/**\brief Enables compilation of code with versioned variables - * \details Dictates order of computation determined by VersionsPass +/**\brief Versioned variables code scope level compilation support + * + * Decorates \ref xreate::compilation::ICodeScopeUnit to enable versioned variables compilation. + * An order of variables computation is determined by \ref VersionsPass. * \extends xreate::compilation::ICodeScopeUnit * \sa VersionsPass, VersionsGraph */ template class VersionsScopeDecorator: public Parent{ typedef VersionsScopeDecorator SELF; public: VersionsScopeDecorator(const CodeScope* const codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} virtual llvm::Value* processSymbol(const Symbol& s, std::string hintSymbol=""){ if (Attachments::exists(s)){ const std::list dependencies = Attachments::get(s); for(const Symbol& symbolDependent: dependencies){ processSymbol(symbolDependent); } } llvm::Value* result = Parent::processSymbol(s, hintSymbol); if (s.identifier.version == VERSION_INIT){ llvm::Value* storage = SELF::processIntrinsicInit(result->getType(), hintSymbol); setSymbolStorage(s, storage); processIntrinsicCopy(result, storage); return compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage); } else if (s.identifier.version != VERSION_NONE){ Symbol symbolInitVersion = getSymbolInitVersion(s); llvm::Value* storage = getSymbolStorage(symbolInitVersion); processIntrinsicCopy(result, storage); return compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage); } return result; } llvm::Value* processIntrinsicInit(llvm::Type* typeStorage, const std::string& hintVarDecl=""){ LLVMLayer* llvm = compilation::ICodeScopeUnit::pass->man->llvm; llvm::IntegerType* tyInt = llvm::Type::getInt32Ty(llvm->llvmContext); llvm::ConstantInt* constOne = llvm::ConstantInt::get(tyInt, 1, false); return llvm->builder.CreateAlloca(typeStorage, constOne, hintVarDecl); } void processIntrinsicCopy(llvm::Value* value, llvm::Value* storage){ compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateStore(value, storage); } private: std::map __symbolStorage; static Symbol getSymbolInitVersion(const Symbol& s){ return Symbol{ScopedSymbol{s.identifier.id, VERSION_INIT}, s.scope}; } llvm::Value* getSymbolStorage(const Symbol& s){ return __symbolStorage.at(s); } void setSymbolStorage(const Symbol& s, llvm::Value* storage){ __symbolStorage[s] = storage; } }; +/** + * \brief A Versions-aware function level decorator. Not used currently. + */ template class VersionedFunctionDecorator : public Parent { public: VersionedFunctionDecorator(ManagedFnPtr f, CompilePass* p) : Parent(f, p){} protected: std::vector prepareArguments() { std::vector&& arguments = Parent::prepareArguments(); return arguments; } }; -} } //end of namespace xreate::versions - -// llvm::Value* -// processIntrinsicInitAndCopy(){ -// -// } - -//llvm::Value* -//process(const Expression& expr, const std::string& hintVarDecl){ -// case Operator::CALL_INTRINSIC: { -// enum INRINSIC{INIT, COPY}; -// -// const ExpandedType& typSymbol = pass->man->root->expandType(expr.type); -// -// INTRINSIC op = (INTRINSIC) expr.getValueDouble(); -// -// switch (op){ -// case INIT: { -// llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol); -// -// -// return storage; -// } -// -// case COPY: { -// llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol); -// llvm::value* valueOriginal = process(expr.getOperands()[0], hintVarDecl); -// llvm::Value* storage = l.builder.CreateAlloca(typSymbolRaw, constOne, hintVarDecl); -// llvm::Value* valueCopy = l.builder.CreateStore(valueOriginal, storage); -// -// return valueCopy; -// } -// } -// return; -// } -//} -//}; +} } //end of namespace xreate::versions \ No newline at end of file diff --git a/cpp/src/llvmlayer.cpp b/cpp/src/llvmlayer.cpp index 6cca735..846d96c 100644 --- a/cpp/src/llvmlayer.cpp +++ b/cpp/src/llvmlayer.cpp @@ -1,244 +1,242 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * llvmlayer.cpp * * Author: pgess */ /** * \file llvmlayer.h - * \brief Wrapper over LLVM + * \brief Bytecode generation */ #include "ast.h" #include "llvmlayer.h" #include "ExternLayer.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/Support/TargetSelect.h" #include #include using namespace llvm; using namespace xreate; using namespace std; LLVMLayer::LLVMLayer(AST* root) : llvmContext(), builder(llvmContext), ast(root), module(new llvm::Module(root->getModuleName(), llvmContext)) { layerExtern = new ExternLayer(this); layerExtern->init(root); } void* LLVMLayer::getFunctionPointer(llvm::Function* function) { uint64_t entryAddr = jit->getFunctionAddress(function->getName().str()); return (void*) entryAddr; } void LLVMLayer::initJit() { std::string ErrStr; LLVMInitializeNativeTarget(); llvm::InitializeNativeTargetAsmPrinter(); llvm::EngineBuilder builder(std::unique_ptr(module.release())); jit.reset(builder .setEngineKind(llvm::EngineKind::JIT) .setErrorStr(&ErrStr) .setVerifyModules(true) .create() ); } void LLVMLayer::print() { llvm::PassManager PM; PM.addPass(llvm::PrintModulePass(llvm::outs(), "banner")); llvm::AnalysisManager aman; PM.run(*module.get(), aman); } void LLVMLayer::moveToGarbage(void *o) { __garbage.push_back(o); } llvm::Type* LLVMLayer::toLLVMType(const ExpandedType& ty) const { std::map empty; return toLLVMType(ty, empty); } llvm::Type* LLVMLayer::toLLVMType(const ExpandedType& ty, std::map& conjuctions) const { TypeAnnotation t = ty; switch (t.__operator) { case TypeOperator::LIST_ARRAY: { assert(t.__operands.size() == 1); TypeAnnotation elTy = t.__operands.at(0); return llvm::ArrayType::get(toLLVMType(ExpandedType(move(elTy)), conjuctions), t.__size); } case TypeOperator::LIST_RECORD: { std::vector pack_; pack_.reserve(t.__operands.size()); std::transform(t.__operands.begin(), t.__operands.end(), std::inserter(pack_, pack_.end()), [this, &conjuctions](const TypeAnnotation & t) { return toLLVMType(ExpandedType(TypeAnnotation(t)), conjuctions); }); llvm::ArrayRef pack(pack_); //process recursive types: if (conjuctions.count(t.conjuctionId)) { auto result = conjuctions[t.conjuctionId]; result->setBody(pack, false); return result; } return llvm::StructType::get(llvmContext, pack, false); }; case TypeOperator::LINK: { llvm::StructType* conjuction = llvm::StructType::create(llvmContext); int id = t.conjuctionId; conjuctions.emplace(id, conjuction); return conjuction; }; case TypeOperator::CALL: { assert(false); }; case TypeOperator::CUSTOM: { //Look in extern types clang::QualType qt = layerExtern->lookupType(t.__valueCustom); return layerExtern->toLLVMType(qt); }; //DEBT omit ID field in case of single variant. case TypeOperator::VARIANT: { /* Variant Type Layout: * { * id :: i8, Holds stored variant id * storage:: type of biggest variant * } */ uint64_t sizeStorage = 0; llvm::Type* typStorageRaw = llvm::Type::getVoidTy(llvmContext); for(const TypeAnnotation& subtype : t.__operands) { llvm::Type* subtypeRaw = toLLVMType(ExpandedType(subtype), conjuctions); if (subtypeRaw->isVoidTy()) continue; uint64_t sizeSubtype = module->getDataLayout().getTypeStoreSize(subtypeRaw); if (sizeSubtype > sizeStorage) { sizeStorage = sizeSubtype; typStorageRaw = subtypeRaw; } } std::vector layout; layout.push_back(llvm::Type::getInt8Ty(llvmContext)); //id const bool flagHoldsData = sizeStorage > 0; if (flagHoldsData) { layout.push_back(typStorageRaw); //storage } return llvm::StructType::get(llvmContext, llvm::ArrayRef(layout)); } case TypeOperator::NONE: { switch (t.__value) { case TypePrimitive::I32: case TypePrimitive::Int: case TypePrimitive::Num: return llvm::Type::getInt32Ty(llvmContext); case TypePrimitive::Bool: return llvm::Type::getInt1Ty(llvmContext); case TypePrimitive::I8: return llvm::Type::getInt8Ty(llvmContext); case TypePrimitive::I64: return llvm::Type::getInt64Ty(llvmContext); case TypePrimitive::Float: return llvm::Type::getDoubleTy(llvmContext); case TypePrimitive::String: return llvm::Type::getInt8PtrTy(llvmContext); case TypePrimitive::Invalid: return llvm::Type::getVoidTy(llvmContext); default: assert(false); } } default: assert(false); } assert(false); return nullptr; } bool TypeUtils::isStruct(const ExpandedType& ty) { const TypeAnnotation& t = ty.get(); if (t.__operator == TypeOperator::LIST_RECORD) { return true; } if (t.__operator != TypeOperator::CUSTOM) { return false; } clang::QualType tqual = llvm->layerExtern->lookupType(t.__valueCustom); const clang::Type * raw = tqual.getTypePtr(); // TODO skip ALL the pointers until non-pointer type found if (raw->isStructureType()) return true; if (!raw->isAnyPointerType()) return false; clang::QualType pointee = raw->getPointeeType(); return pointee->isStructureType(); } bool TypeUtils::isPointer(const ExpandedType &ty) { if (ty.get().__operator != TypeOperator::CUSTOM) return false; clang::QualType qt = llvm->layerExtern->lookupType(ty.get().__valueCustom); return llvm->layerExtern->isPointer(qt); } std::vector TypeUtils::getStructFields(const ExpandedType &t) { return (t.get().__operator == TypeOperator::LIST_RECORD) ? t.get().fields : llvm->layerExtern->getStructFields( llvm->layerExtern->lookupType(t.get().__valueCustom)); -} - - +} \ No newline at end of file diff --git a/cpp/src/llvmlayer.h b/cpp/src/llvmlayer.h index 900bb0d..ab6938d 100644 --- a/cpp/src/llvmlayer.h +++ b/cpp/src/llvmlayer.h @@ -1,69 +1,70 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * llvmlayer.h * * Author: pgess */ #ifndef LLVMLAYER_H #define LLVMLAYER_H #include "llvm/IR/Module.h" #include "llvm/IR/Function.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Verifier.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LLVMContext.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "utils.h" namespace xreate { class AST; class ExternLayer; class TypeAnnotation; +/** \brief A wrapper over LLVM toolchain to generate and execute bytecode */ class LLVMLayer { public: LLVMLayer(AST* rootAST); mutable llvm::LLVMContext llvmContext; llvm::IRBuilder<> builder; AST *ast = 0; ExternLayer *layerExtern =0; std::unique_ptr module; std::unique_ptr jit; void moveToGarbage(void *o); llvm::Type* toLLVMType(const Expanded& ty) const; void print(); void* getFunctionPointer(llvm::Function* function); void initJit(); private: llvm::Type* toLLVMType(const Expanded& ty, std::map& conjunctions) const; std::vector __garbage; }; struct TypeUtils { bool isStruct(const Expanded& ty); bool isPointer(const Expanded& ty); std::vector getStructFields(const Expanded& t); TypeUtils(LLVMLayer*llvmlayer) : llvm(llvmlayer){} private: LLVMLayer* llvm; }; } #endif // LLVMLAYER_H diff --git a/cpp/src/modules.h b/cpp/src/modules.h index 3be3aaa..fbc4ee9 100644 --- a/cpp/src/modules.h +++ b/cpp/src/modules.h @@ -1,76 +1,78 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: modules.h * Author: pgess * * Created on July 22, 2017, 5:11 PM */ #ifndef MODULES_H #define MODULES_H #include "ast.h" #include #ifndef FRIENDS_MODULES_TESTS #define FRIENDS_MODULES_TESTS #endif namespace xreate { namespace modules{ class ModulesSolver; -/**\brief Holds information related to an individual module +/** + * \brief An individual module specific information * \sa XreateManagerDecoratorModules, ModulesSolver, ModulesRegistry */ class ModuleRecord { FRIENDS_MODULES_TESTS friend class ModulesSolver; public: void addRequest(const Expression& request); void addControllerPath(const std::string& path); void addDiscoveryPath(const std::string& path); void addProperty(const Expression& prop); private: std::list __requests; std::list __controllers; std::list __discoveryPaths; std::list __properties; public: std::string __path; }; -/** \brief Resolves module's requrements +/** + * \brief A module's requirements resolver * \sa XreateManagerDecoratorModules, ModuleRecord */ class ModulesSolver{ FRIENDS_MODULES_TESTS public: ModulesSolver(){} /** \brief Loads content of *controllers* into logic program for resolution */ void loadControllers(const ModuleRecord& module); /** \brief Discovers specified path for existing modules and stores found modules' properties */ void discoverModules(const ModuleRecord& moduleClient); void extractProperties(const ModuleRecord& module); void extractRequirements(const ModuleRecord& module); void add(const std::string& base); void init(const std::string& programBase, const ModuleRecord& module); std::list run(const ModuleRecord& module); public: std::ostringstream __program; }; }} //end namespace xreate::modules #endif /* MODULES_H */ diff --git a/cpp/src/pass/abstractpass.cpp b/cpp/src/pass/abstractpass.cpp index 9cc6285..26b3195 100644 --- a/cpp/src/pass/abstractpass.cpp +++ b/cpp/src/pass/abstractpass.cpp @@ -1,103 +1,101 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess */ /** * \file abstractpass.h * \brief Infrastructure to iterate over AST to facilitate analysis and compilation. */ #include "abstractpass.h" #include "attachments.h" #include "xreatemanager.h" using namespace std; namespace xreate { template<> void defaultValue(){} void IPass::finish(){} IPass::IPass(PassManager *manager) : man(manager) { } template<> void AbstractPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol) { if (__visitedSymbols.isCached(symbol)) return; __visitedSymbols.setCachedValue(symbol); const Expression& declaration = CodeScope::getDefinition(symbol, true); if (declaration.isDefined()){ PassContext context2 = context.updateScope(symbol.scope); process(declaration, context2, hintSymbol); } } template<> void AbstractPass::process(const Expression& expression, PassContext context, const std::string& varDecl){ if (expression.__state == Expression::COMPOUND){ for (const Expression &op: expression.getOperands()) { process(op, context); } for (CodeScope* scope: expression.blocks) { process(scope, context); } if (expression.op == Operator::CALL){ processExpressionCall(expression, context); } return; } if (expression.__state == Expression::IDENT){ assert(context.scope); processSymbol(Attachments::get(expression), context, expression.getValueString()); } } } /** * \class xreate::IPass * - * Each pass has to have IPass interface to be controllable by XreateManager. - * However in most cases users should inherit minimal useful implementation xreate::AbstractPass + * An each pass has to have IPass interface to be controllable by \ref XreateManager. + * However in most cases users should inherit minimal useful implementation \ref xreate::AbstractPass * - * \note It's usually expected that custom Pass publish processing results by one of the following means: - * - xreate::Attachments for communicating with other Passes - * - IAnalysisReport to feed xreate::TranscendLayer solver + * \note It's usually expected that a custom pass publishes processing results by one of the following means: + * - xreate::Attachments for communicating with other passes + * - IAnalysisReport to prepare date for the \ref xreate::TranscendLayer solver * * \sa xreate::XreateManager, xreate::AbstractPass */ /** * \class xreate::AbstractPass * - * Iterates over %AST and provides functions to alter processing of particular %AST nodes. - * Thus client should not re-implement every possible node processing - * and it's enough to focus only on relevant nodes. + * The class traverses the %AST in a minimally useful manner sparing clients from processing an every possible node manually. + * This way clients able to focus only on relevant nodes processing. * - * Template parameter `Output` specify type of node processing result data. + * The `Output` template parameter specifies the type of node processing result data. * - * Automatically caches already visited nodes - * - * \note It's usually expected that custom Pass publish processing results by one of the following means: - * - xreate::Attachments for communicating with other Passes - * - IAnalysisReport to feed xreate::TranscendLayer solver + * \note Automatically caches already visited nodes * + * \note It's usually expected that a custom pass publishes processing results by one of the following means: + * - \ref xreate::Attachments for communicating with other passes + * - IAnalysisReport to prepare date for the \ref xreate::TranscendLayer solver * * \sa xreate::XreateManager, xreate::IPass, xreate::AST */ diff --git a/cpp/src/pass/abstractpass.h b/cpp/src/pass/abstractpass.h index 77e1f0b..ea44e23 100644 --- a/cpp/src/pass/abstractpass.h +++ b/cpp/src/pass/abstractpass.h @@ -1,205 +1,206 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess */ #ifndef ABSTRACTPASS_H #define ABSTRACTPASS_H #include "ast.h" #include "xreatemanager.h" #include namespace xreate { -/** \brief Holds current position in %AST while traversing*/ + +/** \brief Holds current position in the %AST during traverse*/ struct PassContext { const CodeScope* scope = 0; ManagedFnPtr function; ManagedRulePtr rule; std::string varDecl; PassContext() {} PassContext updateScope(const CodeScope* scopeNew) { PassContext context2{*this}; context2.scope = scopeNew; return context2; } ~PassContext() {} }; -/** \brief Base class for all passes to inherit */ +/** \brief Interface for all passes to inherit. \ref xreate::PassManager holds a collection of passes to execute */ class IPass { public: IPass(PassManager* manager); virtual ~IPass() {} /** \brief Executes pass */ virtual void run() = 0; /** \brief Finalizes pass. Empty by default*/ virtual void finish(); PassManager* man; }; template Output defaultValue(); template<> void defaultValue(); -/** \brief Stores processing results for already visited nodes */ +/** \brief Stores processing results for the already visited nodes */ template class SymbolCache : private std::map { public: bool isCached(const Symbol &symbol) { return this->count(symbol); } Output setCachedValue(const Symbol &symbol, Output &&value) { (*this)[symbol] = value; return value; } Output getCachedValue(const Symbol &symbol) { assert(this->count(symbol)); return this->at(symbol); } }; -/** \brief Set of already visited nodes */ +/** \brief Set of the already visited nodes */ template<> class SymbolCache : private std::set { public: bool isCached(const Symbol &symbol) { bool result = this->count(symbol) > 0; return result; } void setCachedValue(const Symbol &symbol) { this->insert(symbol); } void getCachedValue(const Symbol &symbol) { } }; -/** \brief Minimal useful IPass implementation*/ +/** \brief Minimal \ref xreate::IPass implementation useful for many passes as the base class*/ template class AbstractPass : public IPass { SymbolCache __visitedSymbols; protected: virtual Output processSymbol(const Symbol &symbol, PassContext context, const std::string &hintSymbol = "") { if(__visitedSymbols.isCached(symbol)) return __visitedSymbols.getCachedValue(symbol); const Expression &declaration = CodeScope::getDefinition(symbol, true); if(declaration.isDefined()) { PassContext context2 = context.updateScope(symbol.scope); Output &&result = process(declaration, context2, hintSymbol); return __visitedSymbols.setCachedValue(symbol, std::move(result)); } return defaultValue(); } Output processExpressionCall(const Expression &expression, PassContext context) { const std::string &calleeName = expression.getValueString(); std::list callees = man->root->getFunctionSpecializations(calleeName); //Determined specialization if(callees.size() == 1 && callees.front()) { return processFnCall(callees.front(), context); } //Several specializations or External Fn return processFnCallUncertain(calleeName, callees, context); } SymbolCache &getSymbolCache() { return __visitedSymbols; } public: AbstractPass(PassManager* manager) : IPass(manager) {} /** \brief Processes function invocation instruction */ virtual Output processFnCall(ManagedFnPtr functionCallee, PassContext context) { return defaultValue(); } /** \brief Processes function invocation instruction in uncertain cases * \details Executed when it's impossible statically determine which exactly function is invoked. */ virtual Output processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) { return defaultValue(); } /** \brief Processes Logic Rule */ virtual void process(ManagedRulePtr rule) {} /** \brief Processes Function */ virtual Output process(ManagedFnPtr function) { PassContext context; context.function = function; return process(function->getEntryScope(), context); } /** \brief Processes single CodeScope */ virtual Output process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl = "") { context.scope = scope; return processSymbol(Symbol{ScopedSymbol::RetSymbol, scope}, context); } //TODO expose Symbol instead of varDecl. Required by DFAPass. /** \brief Processes single Expression */ virtual Output process(const Expression &expression, PassContext context, const std::string &varDecl = "") { if(expression.__state == Expression::IDENT) { assert(context.scope); return processSymbol(Attachments::get(expression), context, expression.getValueString()); } assert(false); return defaultValue(); } /** \brief Executes AST traverse */ void run() { ManagedRulePtr rule = man->root->begin(); while(rule.isValid()) { process(rule); ++rule; } ManagedFnPtr f = man->root->begin(); while(f.isValid()) { process(f); ++f; } } }; template<> void AbstractPass::processSymbol(const Symbol &symbol, PassContext context, const std::string &hintSymbol); template<> void AbstractPass::process(const Expression &expression, PassContext context, const std::string &hintSymbol); } #endif diff --git a/cpp/src/pass/cfapass.cpp b/cpp/src/pass/cfapass.cpp index 3756331..5361c4f 100644 --- a/cpp/src/pass/cfapass.cpp +++ b/cpp/src/pass/cfapass.cpp @@ -1,124 +1,125 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * cfapass.cpp * * Author: pgess */ /** * \file cfapass.h * \brief Control Flow Analysis(CFA) */ #include "pass/cfapass.h" #include "analysis/cfagraph.h" #include using namespace std; namespace xreate { namespace cfa { void CFAPass::initSignatures() { auto range = man->root->__interfacesData.equal_range(CFA); for(auto i = range.first; i != range.second; ++i) { __signatures.emplace(i->second.op, i->second); } } void CFAPass::run() { initSignatures(); return AbstractPass::run(); } void CFAPass::finish() { man->transcend->registerReport(__context.graph); return AbstractPass::finish(); } void CFAPass::processFnCall(ManagedFnPtr function, PassContext context) { TranscendLayer* transcend = man->transcend; __context.graph->addCallConnection(transcend->pack(context.scope), function->getName()); return AbstractPass::processFnCall(function, context); } void CFAPass::processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) { TranscendLayer* transcend = man->transcend; __context.graph->addCallConnection(transcend->pack(context.scope), calleeName); return AbstractPass::processFnCallUncertain(calleeName, candidates, context); } void CFAPass::process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl) { TranscendLayer* transcend = man->transcend; const CodeScope* scopeParent = context.scope; ScopePacked scopeId = transcend->pack(scope); __context.graph->addScope(scope); //Parent Relations if(scopeParent) { __context.graph->addParentConnection(scopeId, transcend->pack(scopeParent)); } else { __context.graph->addParentConnection(scopeId, context.function->getName()); } //TOTEST scope annotations //SECTIONTAG context gather scope annotations __context.graph->addScopeAnnotations(scopeId, scope->tags); __context.graph->addContextRules(scopeId, scope->contextRules); return AbstractPass::process(scope, context, hintBlockDecl); } //TOTEST scope annotations via scheme void CFAPass::process(const Expression &expression, PassContext context, const std::string &varDecl) { TranscendLayer* transcend = man->transcend; if(expression.__state == Expression::COMPOUND) { Operator op = expression.op; if(__signatures.count(op)) { assert(expression.blocks.size()); for(const auto &scheme : boost::make_iterator_range(__signatures.equal_range(expression.op))) { __context.graph->addScopeAnnotations(transcend->pack(expression.blocks.front()), scheme.second.getOperands()); } } } return AbstractPass::process(expression, context, varDecl); } void CFAPass::process(ManagedFnPtr function) { __context.graph->addFunctionAnnotations(function->getName(), function->getTags()); return AbstractPass::process(function); } CFAPass::CFAPass(PassManager* manager) : AbstractPass(manager), __context{new CFAGraph(manager->transcend)} { } } } /** * \class xreate::cfa::CFAPass - * \details Provides CFA, important analysis for reasoning. Iterates over AST and stores collected data in CFAGraph + * + * Provides %CFA, an important analysis for the reasoning. Traverses over AST and stores the collected data in \ref xreate::cfa::CFAGraph. */ diff --git a/cpp/src/pass/cfapass.h b/cpp/src/pass/cfapass.h index b4ee3c8..2c3bc3c 100644 --- a/cpp/src/pass/cfapass.h +++ b/cpp/src/pass/cfapass.h @@ -1,51 +1,51 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * cfapass.cpp * Control Flow Graph building pass */ #ifndef CFGPASS_H #define CFGPASS_H #include "xreatemanager.h" #include "transcendlayer.h" #include "abstractpass.h" #include "analysis/cfagraph.h" namespace xreate{namespace cfa { class CFAGraph; -/** \brief Control Flow Analysis Pass(%CFA)*/ +/** \brief %CFA(Control Flow Analysis) implementation */ class CFAPass : public AbstractPass{ public: void process(ManagedFnPtr function) override; void processFnCall(ManagedFnPtr function, PassContext context) override; void processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) override; void process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl="") override; void process(const Expression& expression, PassContext context, const std::string& varDecl="") override; CFAPass(PassManager* manager); void finish() override; void run() override; const CFAGraph* getReport() const {return __context.graph; } protected: struct { CFAGraph* graph; } __context; std::multimap __signatures; //CFA data for particular operators void initSignatures(); }; }} //end of namespace xreate::cfa #endif // CFGPASS_H diff --git a/cpp/src/pass/cfatemporalseqpass.cpp b/cpp/src/pass/cfatemporalseqpass.cpp index 9710e61..9ecca90 100644 --- a/cpp/src/pass/cfatemporalseqpass.cpp +++ b/cpp/src/pass/cfatemporalseqpass.cpp @@ -1,125 +1,120 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: cfatemporalseqpass.cpp * Author: pgess * * Created on February 4, 2019, 4:43 PM */ -/** - * \file cfatemporalseqpass.h - * \brief Context Blocks' compilation order analysis. - */ - #include "pass/cfatemporalseqpass.h" #include "analysis/temporalseqgraph.h" using namespace xreate::cfa; using namespace std; namespace xreate { template<> cfa::Socket defaultValue() { const cfa::TemporalNode invalidNode{(ScopePacked) - 1, cfa::TemporalOperator::EMPTY}; return make_pair(invalidNode, invalidNode); }; } CFATemporalSeqPass::CFATemporalSeqPass(PassManager* manager) : AbstractPass(manager), __graph(new TemporalSeqGraph(manager->transcend)) { } void CFATemporalSeqPass::finish() { man->transcend->registerReport(__graph); return AbstractPass::finish(); } void CFATemporalSeqPass::process( const Expression &expression, PassContext context, const std::string &varDecl) { TranscendLayer* transcend = man->transcend; if(expression.__state == Expression::COMPOUND) { Operator op = expression.op; switch(op) { case Operator::QUERY_LATE: case Operator::MAP: case Operator::FOLD: case Operator::INF: { __graph->addSubScopes(context.scope, expression.blocks); break; } case Operator::IF: case Operator::SWITCH: case Operator::SWITCH_LATE: case Operator::SWITCH_VARIANT: { __graph->addBranchScopes(context.scope, expression.blocks); break; } case Operator::SEQUENCE: { auto socket = __graph->addSocket(context.scope, TemporalOperator::AND); auto nodeFrom = socket.first; for(auto scopeIt = expression.blocks.begin(); scopeIt != expression.blocks.end(); ++scopeIt) { ScopePacked scope = transcend->pack(*scopeIt); TemporalNode node{scope, TemporalOperator::SCOPE}; __graph->connect(nodeFrom, node); nodeFrom = node; } __graph->connect(nodeFrom, socket.second); } default: break; } } return Parent::process(expression, context, varDecl); } void CFATemporalSeqPass::processFnCall(ManagedFnPtr functionCallee, PassContext context) { auto callerSock = __graph->addSocket(context.scope, TemporalOperator::AND); auto calleeSock = __graph->getFnSocket(functionCallee); __graph->connectGuarded(callerSock, calleeSock); } void CFATemporalSeqPass::processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) { auto callerSock = __graph->addSocket(context.scope, TemporalOperator::AND); auto calleeSock = __graph->getUncertainFnSocket(calleeName, candidates); __graph->connectGuarded(callerSock, calleeSock); } void CFATemporalSeqPass::process(ManagedFnPtr function) { TranscendLayer* transcend = man->transcend; Socket fnSocket = __graph->getFnSocket(function); TemporalNode entryNode{ transcend->pack(function->getEntryScope()), TemporalOperator::SCOPE }; __graph->connect(fnSocket.first, entryNode); __graph->connect(entryNode, fnSocket.second); return Parent::process(function); } \ No newline at end of file diff --git a/cpp/src/pass/cfatemporalseqpass.h b/cpp/src/pass/cfatemporalseqpass.h index c5d15e6..95a1dc2 100644 --- a/cpp/src/pass/cfatemporalseqpass.h +++ b/cpp/src/pass/cfatemporalseqpass.h @@ -1,41 +1,47 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: cfatemporalseqpass.h * Author: pgess * * Created on February 4, 2019, 4:43 PM */ +/** + * \file cfatemporalseqpass.h + * \brief Code blocks execution order analysis + */ + #ifndef CFATEMPORALSEQPASS_H #define CFATEMPORALSEQPASS_H #include "xreatemanager.h" #include "abstractpass.h" namespace xreate{namespace cfa { class TemporalSeqGraph; +/** \brief Code blocks execution order analysis. The results are stored in the \ref xreate::cfa::TemporalSeqGraph graph */ class CFATemporalSeqPass: public AbstractPass{ typedef AbstractPass Parent; public: CFATemporalSeqPass(PassManager* manager); void processFnCall(ManagedFnPtr functionCallee, PassContext context) override; void processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) override; void process(const Expression& expression, PassContext context, const std::string& varDecl="") override; void process(ManagedFnPtr function) override; const TemporalSeqGraph* getReport() const {return __graph; } void finish() override; private: TemporalSeqGraph* __graph; }; }} //end of namespace xreate::cfa #endif /* CFATEMPORALSEQPASS_H */ diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp index 7cf3994..55384ef 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,775 +1,780 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * compilepass.cpp */ /** * \file compilepass.h - * \brief Compilation pass + * \brief Main compilation routine. See \ref xreate::CompilePass */ #include "compilepass.h" #include "transcendlayer.h" #include #include "llvmlayer.h" #include "query/containers.h" #include "compilation/containers.h" #include "ExternLayer.h" #include "compilation/targetinterpretation.h" #include "pass/versionspass.h" #include "compilation/scopedecorators.h" #include "compilation/operators.h" #include "compilation/latex.h" #include "analysis/typeinference.h" #include #include #include using namespace std; using namespace llvm; namespace xreate{ namespace compilation{ std::string BasicFunctionUnit::prepareName() { AST* ast = IFunctionUnit::pass->man->root; string name = ast->getFunctionSpecializations(IFunctionUnit::function->__name).size() > 1 ? IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : IFunctionUnit::function->__name; return name; } std::vector BasicFunctionUnit::prepareSignature() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; std::vector signature; std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()), [llvm, ast, entry](const std::string & arg)->llvm::Type* { assert(entry->__identifiers.count(arg)); ScopedSymbol argid{entry->__identifiers.at(arg), versions::VERSION_NONE}; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type)); }); return signature; } llvm::Type* BasicFunctionUnit::prepareResult() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type)); } llvm::Function::arg_iterator BasicFunctionUnit::prepareBindings() { CodeScope* entry = IFunctionUnit::function->__entry; ICodeScopeUnit* entryCompilation = IFunctionUnit::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = IFunctionUnit::raw->arg_begin(); for (std::string &arg : entry->__bindings) { ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, argid); fargsI->setName(arg); ++fargsI; } return fargsI; } //DEBT compiler rigidly depends on exact definition of DefaultFunctionUnit typedef latex::LatexBruteFunctionDecorator< compilation::BasicFunctionUnit> BruteFunctionDefault; ICodeScopeUnit::ICodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : pass(compilePass), function(f), scope(codeScope), currentBlockRaw(nullptr) { } llvm::Value* BruteFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { llvm::Function* calleeInfo = dyn_cast(__callee); if (calleeInfo) { auto argsFormal = calleeInfo->args(); size_t sizeArgsF = std::distance(argsFormal.begin(), argsFormal.end()); assert(args.size() >= sizeArgsF); assert(calleeInfo->isVarArg() || args.size() == sizeArgsF); auto argFormal = argsFormal.begin(); for(size_t argId = 0; argId < args.size(); ++argId){ if(argFormal != argsFormal.end()){ args[argId] = typeinference::doAutomaticTypeConversion( args.at(argId), argFormal->getType(), llvm->builder); ++argFormal; } } } //Do not name function call that returns Void. std::string nameStatement = hintDecl; if (calleeInfo->getReturnType()->isVoidTy()) { nameStatement.clear(); } return llvm->builder.CreateCall(__calleeTy, __callee, args, nameStatement); } //DESABLEDFEATURE implement inlining class CallStatementInline : public IFnInvocation{ public: CallStatementInline(IFunctionUnit* caller, IFunctionUnit* callee, LLVMLayer* l) : __caller(caller), __callee(callee), llvm(l) { } llvm::Value* operator()(std::vector&& args, const std::string& hintDecl) { //TOTEST inlining // CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry); // for(int i=0, size = args.size(); ibindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i))); // } // // // return entryCompilation->compile(); return nullptr; } private: IFunctionUnit* __caller; IFunctionUnit* __callee; LLVMLayer* llvm; bool isInline() { // Symbol ret = Symbol{0, function->__entry}; // bool flagOnTheFly = SymbolAttachments::get(ret, false); //TODO consider inlining return false; } } ; BasicCodeScopeUnit::BasicCodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : ICodeScopeUnit(codeScope, f, compilePass) { } llvm::Value* BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { Expression declaration = CodeScope::getDefinition(s); const CodeScope* scopeExternal = s.scope; ICodeScopeUnit* scopeBruteExternal = ICodeScopeUnit::function->getScopeUnit(scopeExternal); assert(scopeBruteExternal->currentBlockRaw); llvm::Value* resultRaw; llvm::BasicBlock* blockOwn = pass->man->llvm->builder.GetInsertBlock(); if (scopeBruteExternal->currentBlockRaw == blockOwn) { resultRaw = scopeBruteExternal->process(declaration, hintRetVar); scopeBruteExternal->currentBlockRaw = currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); } else { pass->man->llvm->builder.SetInsertPoint(scopeBruteExternal->currentBlockRaw); resultRaw = scopeBruteExternal->processSymbol(s, hintRetVar); pass->man->llvm->builder.SetInsertPoint(blockOwn); } return resultRaw; } IFnInvocation* BasicCodeScopeUnit::findFunction(const Expression& opCall) { const std::string& calleeName = opCall.getValueString(); LLVMLayer* llvm = pass->man->llvm; const std::list& specializations = pass->man->root->getFunctionSpecializations(calleeName); //if no specializations registered - check external function if (specializations.size() == 0) { llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName); llvm::outs() << "Debug/External function: " << calleeName; external->getType()->print(llvm::outs(), true); llvm::outs() << "\n"; return new BruteFnInvocation(external, llvm); } //There should be only one specialization without any valid guards at this point return new BruteFnInvocation(pass->getFunctionUnit( pass->man->root->findFunction(calleeName))->compile(), llvm); } //DISABLEDFEATURE transformations // if (pass->transformations->isAcceptable(expr)){ // return pass->transformations->transform(expr, result, ctx); // } llvm::Value* BasicCodeScopeUnit::process(const Expression& expr, const std::string& hintVarDecl) { #define DEFAULT(x) (hintVarDecl.empty()? x: hintVarDecl) llvm::Value *left; llvm::Value *right; LLVMLayer& l = *pass->man->llvm; xreate::compilation::AdvancedInstructions instructions = xreate::compilation::AdvancedInstructions({this, function, pass}); switch (expr.op) { case Operator::SUB: case Operator::MUL: case Operator::DIV: case Operator::EQU: case Operator::LSS: case Operator::GTR: case Operator::NE: case Operator::LSE: case Operator::GTE: assert(expr.__state == Expression::COMPOUND); assert(expr.operands.size() == 2); left = process(expr.operands[0]); right = process(expr.operands[1]); break; default:; } switch (expr.op) { case Operator::ADD: { left = process(expr.operands[0]); Context context{this, function, pass}; llvm::Value* resultSU = StructUpdate::add(expr.operands[0], left, expr.operands[1], context, DEFAULT("tmp_add")); if (resultSU) return resultSU; right = process(expr.operands[1]); llvm::Value* resultAddPA = pointerarithmetic::PointerArithmetic::add(left, right, context, DEFAULT("tmp_add")); if (resultAddPA) { return resultAddPA; } return l.builder.CreateAdd(left, right, DEFAULT("tmp_add")); break; } case Operator::SUB: return l.builder.CreateSub(left, right, DEFAULT("tmp_sub")); break; case Operator::MUL: return l.builder.CreateMul(left, right, DEFAULT("tmp_mul")); break; case Operator::DIV: if (left->getType()->isIntegerTy()) return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div")); if (left->getType()->isFloatingPointTy()) return l.builder.CreateFDiv(left, right, DEFAULT("tmp_div")); break; case Operator::EQU: { if (left->getType()->isIntegerTy()) return l.builder.CreateICmpEQ(left, right, DEFAULT("tmp_equ")); if (left->getType()->isFloatingPointTy()) return l.builder.CreateFCmpOEQ(left, right, DEFAULT("tmp_equ")); const ExpandedType& leftT = pass->man->root->getType(expr.operands[0]); const ExpandedType& rightT = pass->man->root->getType(expr.operands[0]); if(leftT->__operator == TypeOperator::VARIANT && rightT->__operator == TypeOperator::VARIANT){ llvm::Type* selectorT = llvm::cast(left->getType())->getElementType(0); llvm::Value* leftUnwapped = typeinference::doAutomaticTypeConversion(left, selectorT, l.builder); llvm::Value* rightUnwapped = typeinference::doAutomaticTypeConversion(right, selectorT, l.builder); return l.builder.CreateICmpEQ(leftUnwapped, rightUnwapped, DEFAULT("tmp_equ")); } break; } case Operator::NE: return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne")); break; case Operator::LSS: return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss")); break; case Operator::LSE: return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse")); break; case Operator::GTR: return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr")); break; case Operator::GTE: return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte")); break; case Operator::NEG: left = process(expr.operands[0]); return l.builder.CreateNeg(left, DEFAULT("tmp_neg")); break; case Operator::CALL: { assert(expr.__state == Expression::COMPOUND); shared_ptr callee(findFunction(expr)); const std::string& nameCallee = expr.getValueString(); //prepare arguments std::vector args; args.reserve(expr.operands.size()); std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()), [this](const Expression & operand) { return process(operand); } ); return (*callee)(move(args), DEFAULT("res_" + nameCallee)); } case Operator::IF: { return instructions.compileIf(expr, DEFAULT("tmp_if")); } case Operator::SWITCH: { return instructions.compileSwitch(expr, DEFAULT("tmp_switch")); } case Operator::LOGIC_AND: { assert(expr.operands.size() == 1); return process(expr.operands[0]); } case Operator::LIST: { ExpandedType exprT = l.ast->getType(expr); bool flagIsArray; do { if (exprT->__operator == TypeOperator::CUSTOM){ if (l.layerExtern->isArrayType(exprT->__valueCustom)){ flagIsArray = true; break; } if (l.layerExtern->isRecordType(exprT->__valueCustom)){ flagIsArray = false; break; } assert(false && "Inapproriate external type"); } if (exprT->__operator != TypeOperator::LIST_ARRAY && exprT->__operator != TypeOperator::LIST_RECORD){ assert(false && "Inapproriate type"); } flagIsArray = exprT->__operator == TypeOperator::LIST_ARRAY; } while(false); if(flagIsArray){ return instructions.compileListAsSolidArray(expr, DEFAULT("tmp_list")); } const std::vector fieldsFormal = (exprT.get().__operator == TypeOperator::CUSTOM) ? l.layerExtern->getStructFields(l.layerExtern->lookupType(exprT.get().__valueCustom)) : exprT.get().fields; std::map indexFields; for (size_t i = 0, size = fieldsFormal.size(); i < size; ++i) { indexFields.emplace(fieldsFormal[i], i); } llvm::StructType* tyLiteralRaw = llvm::cast(l.toLLVMType(exprT)); llvm::Value* record = llvm::UndefValue::get(tyLiteralRaw); for (size_t i = 0; i < expr.operands.size(); ++i) { const Expression& operand = expr.operands.at(i); unsigned int fieldId = indexFields.at(expr.bindings.at(i)); llvm::Value* result = process(operand); assert(result); record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef({fieldId})); } return record; }; case Operator::LIST_RANGE: { assert(false); //no compilation phase for a range list // return InstructionList(this).compileConstantArray(expr, l, hintRetVar); }; case Operator::MAP: { assert(expr.blocks.size()); return instructions.compileMapSolidOutput(expr, DEFAULT("map")); }; case Operator::FOLD: { return instructions.compileFold(expr, DEFAULT("fold")); }; case Operator::INF: { return instructions.compileFoldInf(expr, DEFAULT("fold")); }; case Operator::INDEX: { //TASK allow multiindex compilation assert(expr.operands.size() == 2); assert(expr.operands[0].__state == Expression::IDENT); const std::string& hintIdent = expr.operands[0].getValueString(); Symbol s = Attachments::get(expr.operands[0]); const ExpandedType& t2 = pass->man->root->getType(expr.operands[0]); llvm::Value* aggr = processSymbol(s, hintIdent); switch (t2.get().__operator) { case TypeOperator::LIST_RECORD: case TypeOperator::CUSTOM: { std::string idxField; const Expression& idx = expr.operands.at(1); switch (idx.__state) { //named struct field case Expression::STRING: idxField = idx.getValueString(); break; //anonymous struct field case Expression::NUMBER: idxField = to_string((int) idx.getValueDouble()); break; default: assert(false && "Wrong index for a struct"); } return instructions.compileStructIndex(aggr, t2, idxField); }; case TypeOperator::LIST_ARRAY: { std::vector indexes; std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()), [this] (const Expression & op) { return process(op); } ); return instructions.compileArrayIndex(aggr, indexes, DEFAULT(string("el_") + hintIdent)); }; default: assert(false); } }; case Operator::CALL_INTRINSIC: { const std::string op = expr.getValueString(); if (op == "copy") { llvm::Value* result = process(expr.getOperands().at(0)); auto decoratorVersions = Decorators::getInterface(this); llvm::Value* storage = decoratorVersions->processIntrinsicInit(result->getType()); decoratorVersions->processIntrinsicCopy(result, storage); return l.builder.CreateLoad(storage, hintVarDecl); } assert(false && "undefined intrinsic"); } case Operator::QUERY: case Operator::QUERY_LATE: { assert(false && "Should be processed by interpretation"); } case Operator::VARIANT: { const ExpandedType& typVariant = pass->man->root->getType(expr); llvm::Type* typVariantRaw = l.toLLVMType(typVariant); llvm::Type* typIdRaw = llvm::cast(typVariantRaw)->getElementType(0); uint64_t id = expr.getValueDouble(); llvm::Value* variantRaw = llvm::UndefValue::get(typVariantRaw); variantRaw = l.builder.CreateInsertValue(variantRaw, llvm::ConstantInt::get(typIdRaw, id), llvm::ArrayRef({0})); const bool flagDoReference = expr.operands.size(); if (flagDoReference) { const ExpandedType& subtyp = ExpandedType(typVariant->__operands.at(id)); llvm::Type* subtypRaw = l.toLLVMType(subtyp); Attachments::put(expr.operands.at(0), subtyp); llvm::Value* subtypValue = process(expr.operands.at(0)); llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); llvm::Value* addrAsStorage = l.builder.CreateAlloca(typStorageRaw); llvm::Value* addrAsSubtyp = l.builder.CreateBitOrPointerCast(addrAsStorage, subtypRaw->getPointerTo()); l.builder.CreateStore(subtypValue, addrAsSubtyp); llvm::Value* storageRaw = l.builder.CreateLoad(typStorageRaw, addrAsStorage); variantRaw = l.builder.CreateInsertValue(variantRaw, storageRaw, llvm::ArrayRef({1})); } return variantRaw; } case Operator::SWITCH_VARIANT: { return instructions.compileSwitchVariant(expr, DEFAULT("tmpswitch")); } case Operator::SWITCH_LATE: { assert(false && "Instruction's compilation should've been redirected to interpretation"); return nullptr; } case Operator::SEQUENCE: { return instructions.compileSequence(expr); } case Operator::UNDEF: { llvm::Type* typExprUndef = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); return llvm::UndefValue::get(typExprUndef); } case Operator::INVALID: assert(expr.__state != Expression::COMPOUND); switch (expr.__state) { case Expression::IDENT: { Symbol s = Attachments::get(expr); return processSymbol(s, expr.getValueString()); } case Expression::NUMBER: { llvm::Type* typConst = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); int literal = expr.getValueDouble(); if (typConst->isFloatingPointTy()) return llvm::ConstantFP::get(typConst, literal); if (typConst->isIntegerTy()) return llvm::ConstantInt::get(typConst, literal); assert(false && "Can't compile literal"); } case Expression::STRING: { return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str")); }; default: { break; } }; break; default: break; } assert(false && "Can't compile expression"); return 0; } llvm::Value* BasicCodeScopeUnit::compile(const std::string& hintBlockDecl) { LLVMLayer* llvm = pass->man->llvm; if (!hintBlockDecl.empty()) { llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm->llvmContext, hintBlockDecl, function->raw); pass->man->llvm->builder.SetInsertPoint(block); } currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope}; return processSymbol(symbScope); } ICodeScopeUnit::~ICodeScopeUnit() { } IFunctionUnit::~IFunctionUnit() { } llvm::Function* IFunctionUnit::compile() { if (raw != nullptr) return raw; LLVMLayer* llvm = pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; string&& functionName = prepareName(); std::vector&& types = prepareSignature(); llvm::Type* expectedResultType = prepareResult(); llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false); raw = llvm::cast(llvm->module->getOrInsertFunction(functionName, ft)); prepareBindings(); const std::string&blockName = "entry"; llvm::BasicBlock* blockCurrent = builder.GetInsertBlock(); llvm::Value* result = getScopeUnit(function->__entry)->compile(blockName); assert(result); //SECTIONTAG types/convert function ret value builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->builder)); if (blockCurrent) { builder.SetInsertPoint(blockCurrent); } llvm->moveToGarbage(ft); return raw; } ICodeScopeUnit* IFunctionUnit::getScopeUnit(const CodeScope * const scope) { if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result) { return result.get(); } } std::shared_ptr unit(pass->buildCodeScopeUnit(scope, this)); if (scope->__parent != nullptr) { auto parentUnit = Decorators::getInterface(getScopeUnit(scope->__parent)); parentUnit->registerChildScope(unit); } else { __orphanedScopes.push_back(unit); } if (!__scopes.emplace(scope, unit).second) { __scopes[scope] = unit; } return unit.get(); } ICodeScopeUnit* IFunctionUnit::getScopeUnit(ManagedScpPtr scope) { return getScopeUnit(&*scope); } ICodeScopeUnit* IFunctionUnit::getEntry() { return getScopeUnit(function->getEntryScope()); } template<> compilation::IFunctionUnit* CompilePassCustomDecorators ::buildFunctionUnit(const ManagedFnPtr& function) { return new BruteFunctionDefault(function, this); } template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators ::buildCodeScopeUnit(const CodeScope * const scope, IFunctionUnit* function) { return new DefaultCodeScopeUnit(scope, function, this); } } // end of compilation compilation::IFunctionUnit* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { compilation::IFunctionUnit* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } void CompilePass::run() { //Initialization: managerTransformations = new xreate::compilation::TransformationsManager(); targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); //Determine entry function: StaticModel model = man->transcend->query(Config::get("function-entry")); assert(model.size() && "Error: No entry function found"); assert(model.size() == 1 && "Error: Ambiguous entry function"); string nameMain = std::get<0>(TranscendLayer::parse(model.begin()->second)); compilation::IFunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain)); //Compilation itself: entry = unitMain->compile(); } llvm::Function* CompilePass::getEntryFunction() { assert(entry); return entry; } void CompilePass::prepareQueries(TranscendLayer* transcend) { transcend->registerQuery(new containers::Query(), QueryId::ContainersQuery); transcend->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery); transcend->registerQuery(new latex::LatexQuery(), QueryId::LatexQuery); } } //end of namespace xreate /** * \class xreate::CompilePass - * \brief Encapsulates all compilation activities + * \brief The owner of the compilation process. Performs fundamental compilation activities along with the xreate::compilation's routines * - * xreate::CompilePass iterates over xreate::AST tree and produces executable code fed by data(via xreate::Attachments) gathered by previous passes as well as data via queries(xreate::IQuery) from xreate:TranscendLayer reasoner. - * Compilation's done using xreate::LLVMLayer(wrapper over LLVM toolchain) and based on following aspects: - * - Containers support. See \ref compilation/containers.h - * - Late Conext compilation. See xreate::context::LateContextCompiler2 - * - Interpretation support. See xreate::interpretation::TargetInterpretation - * - Loop saturation support. See xreate::compilation::TransformerSaturation - * - External Code access. See xreate::ExternLayer(wrapper over Clang library) + * xreate::CompilePass traverses over xreate::AST tree and produces executable code. + * The pass performs compilation using the following data sources: + * - %Attachments: the data gathered by the previous passes. See \ref xreate::Attachments. + * - Transcend solutions accessible via queries. See \ref xreate::IQuery, \ref xreate::TranscendLayer. + * + * The pass generates a bytecode by employing \ref xreate::LLVMLayer(wrapper over LLVM toolchain). + * Many compilation activities are delegated to more specific routines. Most notable delegated compilation aspects are: + * - Containers support. See \ref xreate::containers. + * - Latex compilation. See \ref xreate::latex. + * - Interpretation support. See \ref xreate::interpretation. + * - Loop saturation support. See \ref xreate::compilation::TransformationsScopeDecorator. + * - External code interaction support. See \ref xreate::ExternLayer (wrapper over Clang library). * * \section adaptability_sect Adaptability - * xreate::CompilePass's architecture provides adaptability by employing: - * - %Function Decorators to alter function-level compilation. See xreate::compilation::IFunctionUnit - * - Code Block Decorators to alter code block level compilation. See xreate::compilation::ICodeScopeUnit - * Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit + * xreate::CompilePass's behaviour can be adapted in several ways: + * - %Function Decorators to alter function-level compilation. See \ref xreate::compilation::IFunctionUnit + * - Code Block Decorators to alter code block level compilation. See \ref xreate::compilation::ICodeScopeUnit. + * Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit * - Targets to allow more versitile extensions. - * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See xreate::compilation::Target - * - %Altering Function invocation. xreate::compilation::ICallStatement + * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See \ref xreate::compilation::Target. + * - Altering %function invocation. See \ref xreate::compilation::IFnInvocation. * - * Client able to construct compiler with desired decorators using xreate::compilation::CompilePassCustomDecorators. - * As a handy alias, `CompilePassCustomDecorators` constructs default compiler + * Clients are free to construct a compiler instantiation with the desired decorators by using \ref xreate::compilation::CompilePassCustomDecorators. + * As a handy alias, `CompilePassCustomDecorators` constructs the default compiler. * */ diff --git a/cpp/src/pass/compilepass.h b/cpp/src/pass/compilepass.h index 7853fb8..dc4aba2 100644 --- a/cpp/src/pass/compilepass.h +++ b/cpp/src/pass/compilepass.h @@ -1,208 +1,210 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * compilepass.h */ #ifndef COMPILEPASS_H #define COMPILEPASS_H #include "abstractpass.h" #include "llvm/IR/Function.h" namespace xreate { class TranscendLayer; class CompilePass; class LLVMLayer; namespace interpretation{ class TargetInterpretation; } } namespace xreate { namespace compilation { class ICodeScopeUnit; class IFunctionUnit; class TransformationsManager; /** \brief Holds current position in %AST while traversing*/ struct Context{ ICodeScopeUnit* scope; IFunctionUnit* function; CompilePass* pass; }; -/** \brief Interface to specify custom way of function invocation - * \details Default implementation is xreate::compilation::RawFnInvocation +/** \brief Interface for custom function invocation operation compilation + * \details Default implementation is xreate::compilation::BruteFnInvocation */ class IFnInvocation { public: - /** \brief Returns result of custom function invocation for given arguments*/ + /** \brief Returns result of custom function invocation for the given arguments*/ virtual llvm::Value* operator() (std::vector&& args, const std::string& hintDecl="") = 0; }; /** \brief Default IFnInvocation implementation */ class BruteFnInvocation: public IFnInvocation{ public: BruteFnInvocation(llvm::Function* callee, LLVMLayer* l) : __callee(callee), __calleeTy(callee->getFunctionType()), llvm(l) {} BruteFnInvocation(llvm::Value* callee, llvm::FunctionType* ty, LLVMLayer* l) : __callee(callee), __calleeTy(ty), llvm(l) {} /** \brief Makes type conversions and returns LLVM call statement with given arguments*/ llvm::Value* operator() (std::vector&& args, const std::string& hintDecl=""); protected: llvm::Value* __callee; llvm::FunctionType* __calleeTy; LLVMLayer* llvm; }; /** \brief Interface to allow modification of CodeScope compilation * \details Default implementation defined in xreate::compilation::DefaultCodeScopeUnit */ class ICodeScopeUnit{ public: CompilePass* const pass; IFunctionUnit* const function; const CodeScope* const scope; llvm::BasicBlock* currentBlockRaw; ICodeScopeUnit(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass); virtual ~ICodeScopeUnit(); virtual llvm::Value* compile(const std::string& hintBlockDecl="")=0; virtual llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="")=0; virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="")=0; virtual Symbol bindArg(llvm::Value* value, std::string&& alias)=0; virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0; virtual void reset() = 0; protected: + + /** \brief For subclasses to implement this method to define a function name resolution*/ virtual IFnInvocation* findFunction(const Expression& opCall)=0; }; /** \brief Minimal useful ICodeScopeUnit implementation suited for inheritance */ class BasicCodeScopeUnit: public ICodeScopeUnit{ public: BasicCodeScopeUnit(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass); llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="") override; llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="") override; llvm::Value* compile(const std::string& hintBlockDecl="") override; protected: IFnInvocation* findFunction(const Expression& opCall) override; }; /** \brief Interface to specify compilation of %Function */ class IFunctionUnit{ public: IFunctionUnit(ManagedFnPtr f, CompilePass* p): function(f), pass(p) {} virtual ~IFunctionUnit(); llvm::Function* compile(); ICodeScopeUnit* getEntry(); ICodeScopeUnit* getScopeUnit(const CodeScope * const scope); ICodeScopeUnit* getScopeUnit(ManagedScpPtr scope); virtual llvm::Type* prepareResult() = 0; ManagedFnPtr function; llvm::Function* raw = nullptr; protected: CompilePass* pass=nullptr; virtual std::string prepareName() = 0; virtual std::vector prepareSignature() = 0; virtual llvm::Function::arg_iterator prepareBindings() = 0; private: std::map> __scopes; std::list> __orphanedScopes; }; /** \brief Minimal useful IFunctionUnit implementation suited for inheritance */ class BasicFunctionUnit: public IFunctionUnit{ public: BasicFunctionUnit(ManagedFnPtr f, CompilePass* p) : IFunctionUnit(f, p) {} protected: std::string prepareName() override; virtual std::vector prepareSignature() override; virtual llvm::Type* prepareResult() override; virtual llvm::Function::arg_iterator prepareBindings() override; }; } // end of namespace compilation class CompilePass : public AbstractPass { friend class compilation::BasicCodeScopeUnit; friend class compilation::IFunctionUnit; public: compilation::TransformationsManager* managerTransformations; interpretation::TargetInterpretation* targetInterpretation; CompilePass(PassManager* manager): AbstractPass(manager) {} /** \brief Executes compilation process */ void run() override; /**\brief Returns compiled specified %Function * \details Executes function compilation or read cache if it's already done */ compilation::IFunctionUnit* getFunctionUnit(const ManagedFnPtr& function); /**\brief Returns compiled main(entry) %Function in program */ llvm::Function* getEntryFunction(); /** \brief Initializes queries required by compiler. See xreate::IQuery, xreate::TranscendLayer */ static void prepareQueries(TranscendLayer* transcend); protected: virtual compilation::IFunctionUnit* buildFunctionUnit(const ManagedFnPtr& function)=0; virtual compilation::ICodeScopeUnit* buildCodeScopeUnit(const CodeScope* const scope, compilation::IFunctionUnit* function)=0; private: //TODO free `functions` in destructor std::map functions; llvm::Function* entry = 0; }; namespace compilation{ /** \brief Constructs compiler with desired %Function and %Code Scope decorators. See adaptability in xreate::CompilePass*/ template class CompilePassCustomDecorators: public ::xreate::CompilePass{ public: CompilePassCustomDecorators(PassManager* manager): ::xreate::CompilePass(manager) {} virtual compilation::IFunctionUnit* buildFunctionUnit(const ManagedFnPtr& function) override{ return new FUNCTION_DECORATOR(function, this); } virtual compilation::ICodeScopeUnit* buildCodeScopeUnit(const CodeScope* const scope, IFunctionUnit* function) override{ return new SCOPE_DECORATOR(scope, function, this); } }; template<> compilation::IFunctionUnit* CompilePassCustomDecorators::buildFunctionUnit(const ManagedFnPtr& function); template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators::buildCodeScopeUnit(const CodeScope* const scope, IFunctionUnit* function); }} //end of namespace xreate::compilation #endif // COMPILEPASS_H diff --git a/cpp/src/pass/dfapass.cpp b/cpp/src/pass/dfapass.cpp index de02dc1..e4adedc 100644 --- a/cpp/src/pass/dfapass.cpp +++ b/cpp/src/pass/dfapass.cpp @@ -1,266 +1,266 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * dfapass.cpp */ /** * \file dfapass.h - * \brief Data Flow Analysis(DFA) + * \brief Data Flow Analysis */ //DEBT DFA represent VersionaPass in declarative form using applyDependencies // applyDependencies(expression, context, cache, decl); //DEBT DFA prepare static annotations and represent InterpretationPass in declarative form // applyStaticAnnotations(expression, context, cache, decl); //DEBT DFA Eliminate dfa schemes #include "pass/dfapass.h" #include "xreatemanager.h" #include "transcendlayer.h" #include #include using namespace std; namespace xreate {namespace dfa { DFAPass::DFAPass(PassManager* manager) : AbstractPass(manager) , graph{new DFAGraph()} , transcend(manager->transcend) { } void DFAPass::processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result) { const string &nameCalleeFunction=expr.getValueString(); //TODO implement processFnCall/Uncertain list variantsCalleeFunction=man->root->getFunctionSpecializations(nameCalleeFunction); vector operands; operands.reserve(expr.getOperands().size()); for(const Expression& arg : expr.getOperands()) { operands.push_back(process(arg, context)); } //Set calling relations: DFACallInstanceType type=variantsCalleeFunction.size()>1?WEAK:STRONG; for(ManagedFnPtr function : variantsCalleeFunction) { CodeScope *scopeRemote=function->getEntryScope(); DFACallInstance callInstance; callInstance.fnName=function->getName(); callInstance.type=type; std::vector::const_iterator nodeActual=operands.begin(); for(const std::string &identFormal : scopeRemote->__bindings) { const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE}; SymbolPacked symbolFormalPacked=transcend->pack(Symbol{symbolFormal, scopeRemote}, nameCalleeFunction+":"+identFormal); callInstance.args.push_back(std::make_pair(symbolFormalPacked, *nodeActual)); ++nodeActual; } callInstance.retActual=result; SymbolNode retFormal=SymbolNode(transcend->pack(Symbol{ScopedSymbol::RetSymbol, scopeRemote}, nameCalleeFunction+":[ret]")); graph->addCallInstance(std::move(callInstance)); } } void DFAPass::processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache) { cache.operands.reserve(expression.getOperands().size()); for(const Expression &op : expression.getOperands()) { const SymbolNode& subnodeOperand=process(op, context); cache.operands.push_back(subnodeOperand); graph->addDependency(node, subnodeOperand); } cache.blocks.reserve(expression.blocks.size()); for(CodeScope* block : expression.blocks) { const SymbolNode& subnodeBlock=process(block, context); cache.blocks.push_back(subnodeBlock); graph->addDependency(node, subnodeBlock); } } void DFAPass::processAnnotations(const Expression& expression, PassContext context, const SymbolNode& ident){ for (const pair& tag : expression.tags){ graph->printInplaceAnnotation(ident, tag.second); } } SymbolNode DFAPass::process(const Expression& expression, PassContext context, const std::string& varDecl) { SymbolNode result; if(Attachments::exists(expression)){ Symbol varSymbol=Attachments::get(expression); result=transcend->pack(varSymbol, varDecl); } else if(expression.__state==Expression::IDENT&&expression.tags.size()==0){ Symbol varSymbol=Attachments::get(expression); result=transcend->pack(varSymbol, expression.getValueString()); } else { result=SymbolAnonymous{expression.id}; } processAnnotations(expression, context, result); ProcessingCache cache; switch(expression.__state) { case Expression::COMPOUND: { switch(expression.op) { case Operator::CALL: { processCallInstance(expression, context, result); break; } case Operator::IF: { const SymbolNode& scopeA=process(expression.blocks.front(), context, "ifTrue" + std::to_string(expression.id)); const SymbolNode& scopeB=process(expression.blocks.back(), context, "ifFalse" + std::to_string(expression.id)); const SymbolNode& condition=process(expression.operands.at(0), context); graph->addDependency(result, scopeA); graph->addDependency(result, scopeB); graph->addDependency(result, condition); graph->printWeakAlias(result, scopeA); graph->printWeakAlias(result, scopeB); break; } case Operator::SWITCH: case Operator::SWITCH_VARIANT: { for(CodeScope* block : expression.blocks) { const SymbolNode& subnodeBlock=process(block, context, "case"+to_string(block->getBody().id)); graph->addDependency(result, subnodeBlock); graph->printWeakAlias(result, subnodeBlock); } const SymbolNode& condition=process(expression.operands.at(0), context); graph->addDependency(result, condition); break; } case Operator::MAP: processDependencies(result, expression, context, cache); graph->printOperator(Operator::MAP, {result, cache.operands.at(0), cache.blocks.at(0)}); break; case Operator::FOLD: processDependencies(result, expression, context, cache); graph->printOperator(Operator::FOLD, {result, cache.operands.at(0), cache.operands.at(1), cache.blocks.at(0)}); break; case Operator::LIST: processDependencies(result, expression, context, cache); graph->printOperator(Operator::LIST, {result}, expression.getOperands().size()); break; case Operator::LIST_RANGE: processDependencies(result, expression, context, cache); graph->printOperator(Operator::LIST_RANGE, {result}); break; case Operator::INDEX: processDependencies(result, expression, context, cache); graph->printOperator(Operator::INDEX, {result, cache.operands.at(0), cache.operands.at(1)}); break; default: { processDependencies(result, expression, context, cache); break; } } break; } case Expression::IDENT: { SymbolNode symbIdent=AbstractPass::process(expression, context, varDecl); if(!(result==symbIdent)){ graph->addDependency(result, symbIdent); graph->printAlias(result, symbIdent); } break; } case Expression::NUMBER: case Expression::STRING: { break; } case Expression::INVALID: case Expression::BINDING: { assert(false); break; } } return result; } SymbolNode DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl) { if (!hintBlockDecl.empty()) { Symbol symbRet{ScopedSymbol::RetSymbol, scope}; transcend->pack(symbRet, hintBlockDecl + ":[ret]"); } for(const std::string& binding : scope->__bindings) { Symbol bindingSymb{scope->getSymbol(binding), scope}; SymbolPacked bindingSymbP=transcend->pack(bindingSymb, binding); getSymbolCache().setCachedValue(bindingSymb, SymbolNode(bindingSymbP)); processAnnotations(scope->getDefinition(bindingSymb), context, SymbolNode(bindingSymbP)); } return AbstractPass::process(scope, context, hintBlockDecl); } SymbolNode DFAPass::process(ManagedFnPtr function) { transcend->pack(Symbol{ScopedSymbol::RetSymbol, function->getEntryScope()}, function->getName()+to_string(function.id())+":[ret]"); SymbolNode result=AbstractPass::process(function); graph->printFunctionRet(function, result); return result; } void DFAPass::finish() { transcend->registerReport(graph); //Declare symbols: graph->printSymbols(transcend); AbstractPass::finish(); } } //end of namespace dfa template<> SymbolNode defaultValue() { assert(false); } } //end of xreate namespace /** * \class xreate::dfa::DFAPass - * \details Provides DFA, important analysis for reasoning. Iterates over AST and stores collected data in DFAGraph + * \details Provides DFA, an important analysis for the reasoning. Traverses over AST and stores the collected data in the \ref xreate::dfa::DFAGraph */ diff --git a/cpp/src/pass/dfapass.h b/cpp/src/pass/dfapass.h index 0626a26..f27f6b5 100644 --- a/cpp/src/pass/dfapass.h +++ b/cpp/src/pass/dfapass.h @@ -1,52 +1,52 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * dfapass.h * Data Flow Graph building pass */ #ifndef DFGPASS_H #define DFGPASS_H #include "abstractpass.h" #include "analysis/dfagraph.h" namespace xreate { class TranscendLayer; } namespace xreate { namespace dfa { struct ProcessingCache { std::vector operands; std::vector blocks; }; -/** \brief Data Flow Analysis Pass(%DFA) */ +/** \brief Data Flow Analysis Pass */ class DFAPass: public AbstractPass { public: DFAPass(PassManager* manager); protected: virtual void processAnnotations(const Expression& expression, PassContext context, const SymbolNode& ident); virtual SymbolNode process(const Expression& expression, PassContext context, const std::string& varDecl="") override; virtual SymbolNode process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl="") override; virtual SymbolNode process(ManagedFnPtr function) override; void init(); void finish() override; DFAGraph* graph; TranscendLayer* transcend; private: void processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result); void processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache); }; }} //end of xreate::dfa namespace #endif diff --git a/cpp/src/pass/interpretationpass.cpp b/cpp/src/pass/interpretationpass.cpp index 9143ecb..e4b1265 100644 --- a/cpp/src/pass/interpretationpass.cpp +++ b/cpp/src/pass/interpretationpass.cpp @@ -1,554 +1,554 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: interpretationpass.cpp * Author: pgess * * Created on July 5, 2016, 5:21 PM */ /** * \file interpretationpass.h - * \brief Interpretation analysis: determines what parts of code could be interpreted + * \brief Interpretation analysis: determines what parts of a code could be interpreted */ #include "pass/interpretationpass.h" #include #include #include "ast.h" //DEBT implement InterpretationPass purely in transcend //DEBT represent InterpretationPass as general type inference using namespace std; namespace xreate { template<> interpretation::InterpretationResolution defaultValue() { return interpretation::CMPL_ONLY; } namespace interpretation { enum InterpretationQuery { QUERY_INTR_ONLY, QUERY_CMPL_ONLY }; namespace details { template bool checkConstraints(InterpretationResolution flag) { return( (flag==INTR_ONLY&&FLAG_REQUIRED==QUERY_INTR_ONLY) ||(flag==CMPL_ONLY&&FLAG_REQUIRED==QUERY_CMPL_ONLY)); } InterpretationResolution recognizeTags(const map& tags) { auto i=tags.find("i12n"); if(i==tags.end()){ return ANY; } assert(i->second.op==Operator::CALL); const string& cmd=i->second.operands.at(0).getValueString(); //TODO make consistent names of annotation and resolution if(cmd=="on"){ return INTR_ONLY; } else if(cmd=="off"){ return CMPL_ONLY; } return ANY; } } InterpretationResolution unify(InterpretationResolution flag) { return flag; } template InterpretationResolution unify(FLAG_A flagA, FLAG_B flagB, FLAGS... flags) { if(flagA==ANY){ return unify(flagB, flags...); } if(flagB==ANY){ return unify(flagA, flags...); } assert(flagA==flagB); return flagA; } template bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); return details::checkConstraints(flag); } template bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); flags.pop_back(); if(details::checkConstraints(flag)){ return checkConstraints(move(flags)); } return false; } bool InterpretationData::isDefault() const { return(resolution==ANY&&op==NONE); } void recognizeTags(const Expression& e) { InterpretationData tag{details::recognizeTags(e.tags), NONE}; if(!tag.isDefault()) Attachments::put(e, tag); } InterpretationResolution recognizeTags(const ManagedFnPtr& f) { return details::recognizeTags(f->getTags()); } InterpretationPass::InterpretationPass(PassManager* manager) : AbstractPass(manager) { Attachments::init(); Attachments::init(); } void InterpretationPass::run() { ManagedFnPtr f=man->root->begin(); auto& visitedSymbols=getSymbolCache(); while(f.isValid()) { const Symbol&symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; if(!visitedSymbols.isCached(symbolFunction)){ visitedSymbols.setCachedValue(symbolFunction, process(f)); } ++f; } } InterpretationResolution InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl) { recognizeTags(expression); InterpretationResolution resolution=ANY; InterpretationOperator opNo=NONE; switch(expression.__state) { case Expression::NUMBER: case Expression::STRING: { break; } case Expression::IDENT: { resolution=Parent::processSymbol(Attachments::get(expression), context); break; } case Expression::COMPOUND: break; default: { resolution=CMPL_ONLY; break; } } if(expression.__state==Expression::COMPOUND) switch(expression.op) { case Operator::EQU: case Operator::NE: { InterpretationResolution left=process(expression.operands[0], context); InterpretationResolution right=process(expression.operands[1], context); resolution=unify(left, right); break; } case Operator::LOGIC_AND: { assert(expression.operands.size()==1); resolution=process(expression.operands[0], context); break; } case Operator::CALL: { size_t sizeOperands = expression.operands.size(); std::vector operands; operands.reserve(sizeOperands); for(size_t opNo=0; opNo callees=man->root->getFunctionSpecializations(expression.getValueString()); if(callees.size()!=1){ resolution=CMPL_ONLY; break; } ManagedFnPtr callee=callees.front(); const Symbol& symbCalleeFunc{ScopedSymbol::RetSymbol, callee->getEntryScope()}; //recursion-aware processing: // - skip self recursion const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, context.function->getEntryScope()}; if(!(symbSelfFunc==symbCalleeFunc)){ InterpretationResolution resCallee=processFnCall(callee, context); assert(resCallee!=FUNC_POSTPONED&&"Indirect recursion detected: can't decide on interpretation resolution"); resolution=unify(resolution, resCallee); } //check arguments compatibility const FunctionInterpretationData& calleeSignature=FunctionInterpretationHelper::getSignature(callee); for(size_t opNo=0; opNo__identifiers.at(argName), versions::VERSION_NONE}, exprBody }; getSymbolCache().setCachedValue(argS, INTR_ONLY); Parent::process(expression.blocks.front(), context); resolution = CMPL_ONLY; opNo=QUERY_LATE; break; } case Operator::SWITCH_LATE: { resolution = CMPL_ONLY; opNo = SWITCH_LATE; break; } case Operator::IF: { InterpretationResolution flagCondition=process(expression.getOperands()[0], context); InterpretationResolution flagScope1=Parent::process(expression.blocks.front(), context); InterpretationResolution flagScope2=Parent::process(expression.blocks.back(), context); //special case: IF_INTERPRET_CONDITION if(checkConstraints({flagCondition})){ opNo=IF_INTERPRET_CONDITION; flagCondition=ANY; } resolution=unify(flagCondition, flagScope1, flagScope2); break; } case Operator::FOLD: { InterpretationResolution flagInput=process(expression.getOperands()[0], context); InterpretationResolution flagAccumInit=process(expression.getOperands()[1], context); CodeScope* scopeBody=expression.blocks.front(); const std::string& nameEl=expression.bindings[0]; Symbol symbEl{ScopedSymbol {scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody}; getSymbolCache().setCachedValue(symbEl, InterpretationResolution(flagInput)); const std::string& nameAccum=expression.bindings[1]; Symbol symbAccum{ScopedSymbol {scopeBody->__identifiers.at(nameAccum), versions::VERSION_NONE}, scopeBody}; getSymbolCache().setCachedValue(symbAccum, InterpretationResolution(flagAccumInit)); InterpretationResolution flagBody=Parent::process(expression.blocks.front(), context); //special case: FOLD_INTERPRET_INPUT if(checkConstraints({flagInput})){ opNo=FOLD_INTERPRET_INPUT; flagInput=ANY; } resolution=unify(flagInput, flagAccumInit, flagBody); break; } case Operator::INDEX: { for(const Expression &opNo : expression.getOperands()) { resolution=unify(resolution, process(opNo, context)); } break; } case Operator::SWITCH: { InterpretationResolution flagCondition=process(expression.operands[0], context); bool hasDefaultCase=expression.operands[1].op==Operator::CASE_DEFAULT; //determine conditions resolution InterpretationResolution flagHeaders=flagCondition; for(size_t size=expression.operands.size(), i=hasDefaultCase?2:1; i({flagHeaders})){ opNo=SWITCH_INTERPRET_CONDITION; flagHeaders=ANY; } //determine body resolutions resolution=flagHeaders; for(size_t size=expression.operands.size(), i=1; i({resolution})){ opNo=SWITCH_VARIANT; resolution=ANY; } const string identCondition=expression.bindings.front(); for(auto scope : expression.blocks) { //set binding resolution ScopedSymbol symbolInternal=scope->getSymbol(identCondition); getSymbolCache().setCachedValue(Symbol{symbolInternal, scope}, InterpretationResolution(resolutionCondition)); resolution=unify(resolution, Parent::process(scope, context)); } for(auto scope : expression.blocks) { resolution=unify(resolution, Parent::process(scope, context)); } break; } case Operator::LIST: { for(const Expression &opNo : expression.getOperands()) { resolution=unify(resolution, process(opNo, context)); } break; } case Operator::VARIANT: { if(expression.getOperands().size()){ resolution=process(expression.getOperands().front(), context); } else { resolution=ANY; } break; } default: { resolution=CMPL_ONLY; for(const Expression &opNo : expression.getOperands()) { process(opNo, context); } for(CodeScope* scope : expression.blocks) { Parent::process(scope, context); } break; } } InterpretationData dataExpected= Attachments::get(expression,{ANY, NONE}); resolution=unify(resolution, dataExpected.resolution); if(resolution!=dataExpected.resolution || opNo != dataExpected.op ){ Attachments::put(expression,{resolution, opNo}); } return resolution; } InterpretationResolution InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context) { return process(function); } InterpretationResolution InterpretationPass::process(ManagedFnPtr function) { CodeScope* entry=function->getEntryScope(); std::vector arguments=entry->__bindings; const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, function->getEntryScope()}; auto& cache=getSymbolCache(); if(cache.isCached(symbSelfFunc)) return cache.getCachedValue(symbSelfFunc); const FunctionInterpretationData& fnSignature=FunctionInterpretationHelper::getSignature(function); InterpretationResolution fnResolutionExpected=details::recognizeTags(function->getTags()); //mark preliminary function resolution as expected if(fnResolutionExpected!=ANY){ cache.setCachedValue(symbSelfFunc, move(fnResolutionExpected)); } else { // - in order to recognize indirect recursion mark this function resolution as POSTPONED cache.setCachedValue(symbSelfFunc, FUNC_POSTPONED); } //set resolution for function arguments as expected for(int argNo=0, size=arguments.size(); argNo__identifiers.at(arguments[argNo]), versions::VERSION_NONE}, entry}; cache.setCachedValue(symbArg, InterpretationResolution(fnSignature.signature[argNo])); } PassContext context; context.function=function; context.scope=entry; InterpretationResolution resActual=process(CodeScope::getDefinition(symbSelfFunc), context); resActual=unify(resActual, fnResolutionExpected); return cache.setCachedValue(symbSelfFunc, move(resActual)); } const FunctionInterpretationData FunctionInterpretationHelper::getSignature(ManagedFnPtr function) { if(Attachments::exists(function)){ return Attachments::get(function); } FunctionInterpretationData&& data=recognizeSignature(function); Attachments::put(function, data); return data; } FunctionInterpretationData FunctionInterpretationHelper::recognizeSignature(ManagedFnPtr function) { CodeScope* entry=function->__entry; FunctionInterpretationData result; result.signature.reserve(entry->__bindings.size()); bool flagPartialInterpretation=false; for(size_t no=0, size=entry->__bindings.size(); no__bindings[no]; Symbol symbArg{ScopedSymbol {entry->__identifiers.at(argName), versions::VERSION_NONE}, entry}; const Expression& arg=CodeScope::getDefinition(symbArg); InterpretationResolution argResolution=details::recognizeTags(arg.tags); flagPartialInterpretation|=(argResolution==INTR_ONLY); result.signature.push_back(argResolution); } result.flagPartialInterpretation=flagPartialInterpretation; return result; } bool FunctionInterpretationHelper::needPartialInterpretation(ManagedFnPtr function) { const FunctionInterpretationData& data=getSignature(function); return data.flagPartialInterpretation; } } } //end of namespace xreate::interpretation /** \class xreate::interpretation::InterpretationPass * - * Encapsulates *Interpretation Analysis* to support [Interpretation Concept](/w/concepts/dfa) + * The class encapsulates *Interpretation Analysis* to support [Interpretation](/d/concepts/interpretation/). * - * Recognizes program functions, expressions, instructions eligible for interpretation - * and stores output in Attachments and Attachments + * It recognizes program functions, expressions, instructions eligible for interpretation + * and stores the output in \ref Attachments and \ref Attachments * - * There are number of instructions currently able to be interpreted: + * There are number of instructions currently eligible for interpretation: * - Basic literals: numbers and strings * - Compounds: lists, structs, variants * - Non-versioned identifiers * - Comparison and logic operators * - %Function calls * - `query` intrinsic function calls * - Branching: `if`, `loop fold`, `switch`, `switch variant` statements * - * Some of those instructions are eligibile for *late interpretation* to allow coupling + * Some of these instructions are eligible also for *late interpretation* to allow coupling * of compiled instructions with interpreted ones, those are: * - Partial function calls * - Branching: `if`, `loop fold`, `switch`, `switch variant` statements * - * \sa xreate::interpretation::TargetInterpretation, [Interpretation Concept](/w/concepts/dfa) + * \sa xreate::interpretation::TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/) */ diff --git a/cpp/src/pass/interpretationpass.h b/cpp/src/pass/interpretationpass.h index 1f47aa2..22702bf 100644 --- a/cpp/src/pass/interpretationpass.h +++ b/cpp/src/pass/interpretationpass.h @@ -1,96 +1,96 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: interpretationpass.h * Author: pgess * * Created on July 5, 2016, 5:21 PM */ #ifndef INTERPRETATIONPASS_H #define INTERPRETATIONPASS_H #include "abstractpass.h" #include #ifndef FRIENDS_INTERPRETATION_TESTS #define FRIENDS_INTERPRETATION_TESTS #endif //TODO refactor interpretation. Get rid of InterpretationOperator, put only one operator - Hybrid/Late. namespace xreate{ namespace interpretation{ enum InterpretationResolution{ANY, INTR_ONLY, CMPL_ONLY, FUNC_POSTPONED}; enum InterpretationOperator{ NONE, IF_INTERPRET_CONDITION, FOLD_INTERPRET_INPUT, QUERY_LATE, SWITCH_INTERPRET_CONDITION, SWITCH_VARIANT, SWITCH_LATE, CALL_INTERPRET_PARTIAL }; struct InterpretationData{ InterpretationResolution resolution; InterpretationOperator op; bool isDefault() const; }; struct FunctionInterpretationData{ typedef std::vector Signature; Signature signature; bool flagPartialInterpretation; }; class FunctionInterpretationHelper { public: static const FunctionInterpretationData getSignature(ManagedFnPtr function); static bool needPartialInterpretation(ManagedFnPtr function); private: static FunctionInterpretationData recognizeSignature(ManagedFnPtr function); }; -/** \brief Determines parts of program eligible for Interpretation. */ +/** \brief Provides the interpretation analysis. Determines parts of a program eligible for Interpretation by \ref xreate::interpretation::TargetInterpretation */ class InterpretationPass: public AbstractPass { typedef AbstractPass Parent; public: InterpretationResolution process(const Expression& expression, PassContext context, const std::string& varDecl="") override; InterpretationResolution process(ManagedFnPtr function); InterpretationResolution processFnCall(ManagedFnPtr function, PassContext context); InterpretationPass(PassManager* manager); void run(); }; namespace details { InterpretationResolution recognizeTags(const std::map& tags); } } //end of namespace interpretation template<> interpretation::InterpretationResolution defaultValue(); template<> struct AttachmentsDict { typedef interpretation::FunctionInterpretationData Data; static const unsigned int key = 5; }; template<> struct AttachmentsDict { typedef interpretation::InterpretationData Data; static const unsigned int key = 3; }; } //end of namespace xreate #endif /* INTERPRETATIONPASS_H */ diff --git a/cpp/src/pass/latereasoningpass.h b/cpp/src/pass/latereasoningpass.h index 3d293b1..a048aca 100644 --- a/cpp/src/pass/latereasoningpass.h +++ b/cpp/src/pass/latereasoningpass.h @@ -1,188 +1,194 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on June 7, 2018, 7:20 PM - * + */ + +/** * \file latereasoningpass.h - * \brief latereasoningpass + * \brief Late Transcend Support */ + #ifndef LATEREASONINGPASS_H #define LATEREASONINGPASS_H #include "pass/dfapass.h" namespace xreate { namespace latereasoning { +/** \brief Keeps track of defined late transcend parameters */ class LateReasoningScope{ public: LateReasoningScope(LateReasoningScope* parent): __parent(parent){ } boost::optional recognizeIdentifier(const std::string& identifier){ //Search identifier in the current scope if(__identifiers.count(identifier)){ return make_pair(identifier, __identifiers.at(identifier)); } //Search in the parent scope if(__parent){ return __parent->recognizeIdentifier(identifier); } return boost::none; } void addIdentifier(std::string idenName, const SymbolPacked& identSymbol){ __identifiers.emplace(idenName, identSymbol); } private: std::map __identifiers; LateReasoningScope *__parent; }; /** - * \note Limitation: Produces late annotation with target as a symbol to which annotation is attached + * \brief Decorates \ref dfa::DFAPass to recognize and extract late annotations + * \extends xreate::dfa::DFAPass + * \note Limitation: Produces late annotation with the symbol the annotation is attached to being a target */ template class LateReasoningDFAPassDecorator: public Parent{ public: LateReasoningDFAPassDecorator(PassManager* manager): Parent(manager){ } void registerLateScope(CodeScope* scope, LateReasoningScope* scopeLate){ __dictScopes.emplace(scope, scopeLate); } private: LateReasoningScope* liftScope(const CodeScope* scope){ while(scope){ if(__dictScopes.count(scope)) return __dictScopes.at(scope); scope = scope->__parent; } return nullptr; } std::list recognizeLateParameters(const Expression& expression, LateReasoningScope* scope){ std::list result; switch(expression.op){ case Operator::CALL: { for(const auto& op: expression.operands){ std::list opResult = recognizeLateParameters(op, scope); result.insert(result.end(), opResult.begin(), opResult.end()); } if(!expression.operands.size()){ if(auto symbolRecognized = scope->recognizeIdentifier(expression.getValueString())){ result.push_back(*symbolRecognized); } } break; } case Operator::NEG: { assert(expression.operands.size() == 1); const Expression &op = expression.operands.at(0); std::list opResult = recognizeLateParameters(op, scope); result.insert(result.end(), opResult.begin(), opResult.end()); }; case Operator::INVALID: { switch(expression.__state){ case Expression::NUMBER: break; default: assert(true); } break; } default: break; } return result; } protected: virtual SymbolNode process(const Expression& expression, PassContext context, const std::string& varDecl="") override{ if(expression.__state == Expression::COMPOUND && expression.op == Operator::SWITCH_LATE){ //Reserve late scope: LateReasoningScope* scopeLate = new LateReasoningScope(liftScope(context.scope)); CodeScope* scopeBody = expression.blocks.front(); registerLateScope(scopeBody, scopeLate); for(const std::string& identLate: expression.bindings){ ScopedSymbol identLateS = scopeBody->getSymbol(identLate); SymbolPacked identLateSP = Parent::man->transcend->pack(Symbol{identLateS, scopeBody}); scopeLate->addIdentifier(identLate, identLateSP); //Assign late identifiers //Assign type(if isn't provided by user) Expression identE = scopeBody->getDefinition(identLateS); if (!identE.type.isValid()){ Expression sourceE = expression.getOperands().at(0); Attachments::put(Symbol{identLateS, scopeBody}, Parent::man->root->getType(sourceE)); } } } return Parent::process(expression, context, varDecl); } virtual void processAnnotations(const Expression& expression, PassContext context, const SymbolNode& ident){ LateReasoningScope* scopeLate = liftScope(context.scope); if(!expression.tags.size()) {return Parent::processAnnotations(expression, context, ident);} if(!scopeLate) {return Parent::processAnnotations(expression, context, ident);} for(const std::pair& tag: expression.tags){ std::list argsLate = recognizeLateParameters(tag.second, scopeLate); if(!argsLate.size()){ //Standard compilation Parent::graph->printInplaceAnnotation(ident, tag.second); } else{ //Late compilation std::list domains; for(const auto& arg: argsLate){ Symbol symbolUnpacked = Parent::man->transcend->unpack(arg.second); ExpandedType typSymbol = Parent::man->root->getType(CodeScope::getDefinition(symbolUnpacked)); assert(typSymbol->__operator == TypeOperator::SLAVE); domains.push_back(typSymbol->__valueCustom); } Parent::graph->printLateAnnotation(ident, tag.second, argsLate, domains); } } } private: std::unordered_map __dictScopes; }; }} #endif /* LATEREASONINGPASS_H */ diff --git a/cpp/src/pass/versionspass.cpp b/cpp/src/pass/versionspass.cpp index 8129686..9a511f2 100644 --- a/cpp/src/pass/versionspass.cpp +++ b/cpp/src/pass/versionspass.cpp @@ -1,376 +1,380 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * versionspass.cpp * * Author: pgess * Created on January 4, 2017, 3:13 PM */ -/** \class xreate::versions::VersionsPass - * Has two parts: - * - Validates correctness of versioned variables with regard to variables lifespan - * - Determines versioned variables computation order - * \sa VersionsScopeDecorator, VersionsGraph, [Versions Concept](/w/concepts/versions) - */ - #include #include "pass/versionspass.h" namespace std{ std::size_t hash::operator()(xreate::versions::SymbolOrPlaceholder const& s) const {return std::hash()(s.symbol) + (s.flagEndOfLifePlaceholder? 9849 : 1);} bool equal_to::operator()(const xreate::versions::SymbolOrPlaceholder& __x, const xreate::versions::SymbolOrPlaceholder& __y) const { return __x.flagEndOfLifePlaceholder == __y.flagEndOfLifePlaceholder && __x.symbol == __y.symbol; } } using namespace std; namespace xreate { template<> std::list defaultValue>(){ return std::list(); }; } namespace xreate{ namespace versions{ inline std::string printSymbol(const SymbolOrPlaceholder& s){ switch(s.flagEndOfLifePlaceholder){ case SYMBOL: return string("(") + std::to_string(s.symbol.identifier.id) + ", "+ std::to_string(s.symbol.identifier.version) + ")"; case PLACEHOLDER: return string("(") + std::to_string(s.symbol.identifier.id) + ", "+ std::to_string(s.symbol.identifier.version) + ")+"; } return ""; } void VersionsGraph::__debug_print(std::ostream& output) const{ for(auto entry: __inferiors){ output << printSymbol(entry.second) << " <-" << printSymbol(entry.first) << "\n"; } } void VersionsGraph::defineEndOfLife(const Symbol& symbol, const Symbol& symbolSuccessor){ if(__dictSuccessors.count(symbol)){ assert("No version branches allowed yet" && false); } const SymbolOrPlaceholder& placeholder = getEndOfLife(symbol); auto inferiorsDeferred = __inferiors.equal_range(placeholder); std::unordered_multimap inferiorsReassigned; for (const auto& inf: boost::make_iterator_range(inferiorsDeferred)){ inferiorsReassigned.emplace(SymbolOrPlaceholder{SYMBOL, symbolSuccessor}, inf.second); } __inferiors.erase(placeholder); __inferiors.insert(inferiorsReassigned.begin(), inferiorsReassigned.end()); __inferiors.emplace(SymbolOrPlaceholder{SYMBOL, symbolSuccessor}, SymbolOrPlaceholder{SYMBOL, symbol}); __dictSuccessors.emplace(symbol, symbolSuccessor); } SymbolOrPlaceholder VersionsGraph::getEndOfLife(const Symbol& s){ if (__dictSuccessors.count(s)){ return SymbolOrPlaceholder{SYMBOL, __dictSuccessors.at(s)}; } return SymbolOrPlaceholder{PLACEHOLDER, s}; } void VersionsGraph::applyNatualDependencies(const Symbol& symbol, const std::list& dependencies){ for (const Symbol& right: dependencies){ __inferiorsNatural.emplace(symbol, right); } } void VersionsGraph::applyDependentEndOfLife(const SymbolOrPlaceholder& symbol, const list& dependencies){ for (const Symbol& right: dependencies){ auto rightEOF = getEndOfLife(right); __inferiors.emplace(rightEOF, symbol); } } bool VersionsGraph::tryEliminateEofAliases(const std::list& aliases){ if (aliases.size()==1){ return true; } boost::optional symbolActualEoF; for(const SymbolOrPlaceholder alias: aliases){ switch(alias.flagEndOfLifePlaceholder){ case SYMBOL: if(symbolActualEoF){ return false; } symbolActualEoF = alias.symbol; break; case PLACEHOLDER: continue; } } if(!symbolActualEoF){ return false; } for(const SymbolOrPlaceholder alias: aliases){ switch(alias.flagEndOfLifePlaceholder){ case SYMBOL: continue; case PLACEHOLDER: defineEndOfLife(alias.symbol, symbolActualEoF.get()); break; } } return true; } std::list VersionsGraph::extractCycle(const Path& path, const SymbolOrPlaceholder& symbolBeginning){ unsigned int posBeginning = path.at(symbolBeginning); std::list result; auto i=path.begin(); while(true){ i = std::find_if(i, path.end(), [&posBeginning](const auto& el){return el.second >=posBeginning;}); if (i!= path.end()){ result.push_back(i->first); ++i; } else {break; } } return result; } bool VersionsGraph::validateCycles(const SymbolOrPlaceholder& s, std::unordered_multimap& graph, std::unordered_set& symbolsVisited, Path& path) { if (symbolsVisited.count(s)) return true; symbolsVisited.insert(s); path.emplace(s, path.size()); if (graph.count(s)){ //iterate over imposed dependencies auto candidates = graph.equal_range(s); for (auto candidate = candidates.first; candidate != candidates.second; ++candidate){ if (path.count(candidate->second)) { std::list cycle = extractCycle(path, candidate->second); if (!tryEliminateEofAliases(cycle)) return false; continue; } if(!validateCycles(candidate->second, graph, symbolsVisited, path)) return false; } } //iterate over natural dependencies if (s.flagEndOfLifePlaceholder == SYMBOL) { auto candidates = __inferiorsNatural.equal_range(s.symbol); for (auto candidate = candidates.first; candidate != candidates.second; ++candidate){ if (path.count(SymbolOrPlaceholder{SYMBOL, candidate->second})){ return false; } if(!validateCycles(SymbolOrPlaceholder{SYMBOL,candidate->second}, graph, symbolsVisited, path)) return false; } } //check previous version if (s.flagEndOfLifePlaceholder == PLACEHOLDER){ const Symbol& candidate = s.symbol; if (path.count(SymbolOrPlaceholder{SYMBOL, candidate})){ std::list cycle = extractCycle(path, SymbolOrPlaceholder{SYMBOL, candidate}); if (!tryEliminateEofAliases(cycle)) return false; } if(!validateCycles(SymbolOrPlaceholder{SYMBOL,candidate}, graph, symbolsVisited, path)) return false; } path.erase(s); return true; } bool VersionsGraph::validateCycles(){ std::unordered_set symbolsVisited; Path path; std::unordered_multimap graph(__inferiors); std::unordered_multimap::const_iterator s; for (s = graph.begin(); s != graph.end(); ++s){ if(!validateCycles(s->first, graph, symbolsVisited, path)) return false; } return true; } bool VersionsGraph::validate(){ return validateCycles(); } std::list VersionsGraph::expandPlaceholder(const SymbolOrPlaceholder& symbol, const Symbol& symbolPrev) const{ std::list result; switch (symbol.flagEndOfLifePlaceholder){ case SYMBOL: //skip self-loops if (symbol.symbol == symbolPrev) return {}; return {symbol.symbol}; case PLACEHOLDER: for (const auto& entry: boost::make_iterator_range(__inferiors.equal_range(symbol))){ list&& childResult = expandPlaceholder(entry.second, symbolPrev); result.insert(result.end(), childResult.begin(), childResult.end()); } if (__dictSuccessors.count(symbol.symbol)){ Symbol knownSuccessor = __dictSuccessors.at(symbol.symbol); //skip alias loop if (knownSuccessor == symbolPrev) return {}; for (const auto& entry: boost::make_iterator_range(__inferiors.equal_range(SymbolOrPlaceholder{SYMBOL, knownSuccessor}))){ list&& childResult = expandPlaceholder(entry.second, knownSuccessor); result.insert(result.end(), childResult.begin(), childResult.end()); } } break; } return result; } AttachmentsContainerDefault>* VersionsGraph::representAsAttachments() const { AttachmentsContainerDefault>* container = new AttachmentsContainerDefault>(); std::map> containerData; for(const auto& entry: __inferiors){ if(entry.first.flagEndOfLifePlaceholder == PLACEHOLDER) continue; list& infs = containerData[entry.first.symbol]; list&& infsExpanded = expandPlaceholder(entry.second, entry.first.symbol); infs.insert(infs.begin(), infsExpanded.begin(), infsExpanded.end()); } for(const auto& entry: containerData){ container->put(entry.first, entry.second); } return container; } std::list VersionsPass::process(const Expression& expression, PassContext context, const std::string& hintSymbol){ if (expression.__state == Expression::COMPOUND){ std::list resultDependencies; for (const Expression &op: expression.getOperands()) { std::list deps = process(op, context); resultDependencies.insert(resultDependencies.end(), deps.begin(), deps.end()); } for (CodeScope* scope: expression.blocks) { std::list deps = Parent::process(scope, context); resultDependencies.insert(resultDependencies.end(), deps.begin(), deps.end()); } return resultDependencies; } if (expression.__state == Expression::IDENT){ const Symbol symb = Attachments::get(expression); return processSymbol(symb, context, expression.getValueString()); } return {}; } //TODO versions, check (declaration.isDefined()) before processing declaration list VersionsPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol){ list result{symbol}; if (__symbolsVisited.exists(symbol)){ return result; } enum {MODE_ALIAS, MODE_COPY } mode = MODE_ALIAS; const Expression& declaration = CodeScope::getDefinition(symbol); if (declaration.op == Operator::CALL_INTRINSIC){ if (declaration.getValueString() == "copy"){ mode = MODE_COPY; } } if (symbol.identifier.version != VERSION_NONE){ mode = MODE_COPY; if (symbol.identifier.version > 0){ Symbol versionPrev = Symbol{ScopedSymbol{symbol.identifier.id, symbol.identifier.version-1}, symbol.scope}; __graph.defineEndOfLife(versionPrev, symbol); } } PassContext context2 = context.updateScope(symbol.scope); std::list dependencies = process(declaration, context2, hintSymbol); switch (mode) { case MODE_COPY: __graph.applyDependentEndOfLife(SymbolOrPlaceholder{SYMBOL, symbol}, dependencies); break; case MODE_ALIAS: __graph.applyDependentEndOfLife(__graph.getEndOfLife(symbol), dependencies); break; } __graph.applyNatualDependencies(symbol, dependencies); __symbolsVisited.put(symbol, true); return list{symbol}; } VersionsGraph& VersionsPass::getResultGraph(){ return __graph; } void VersionsPass::finish(){ assert(__graph.validate() && "Can't validate versions graph"); Attachments::init(__graph.representAsAttachments()); } }} //end of namespace xreate::versions + + +/** + * \class xreate::versions::VersionsPass + * + * Consists of two parts: + * - Validates correctness of versioned variables usage with regard to the variables lifespans. + * - Determines versioned variables computation order. + * + * \sa VersionsScopeDecorator, VersionsGraph, [Versions Explanation](/d/concepts/versions) + */ \ No newline at end of file diff --git a/cpp/src/pass/versionspass.h b/cpp/src/pass/versionspass.h index 2ce01b6..103d801 100644 --- a/cpp/src/pass/versionspass.h +++ b/cpp/src/pass/versionspass.h @@ -1,119 +1,120 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: versionspass.h * Author: v.melnychenko@xreate.org * * Created on January 4, 2017, 3:09 PM */ /** * \file versionspass.h - * \brief Versions analysis: validates versioned variables' usage and lifespan + * \brief The versions analysis pass */ #ifndef VERSIONSPASS_H #define VERSIONSPASS_H #include "pass/abstractpass.h" #include #include namespace xreate { namespace versions { struct SymbolOrPlaceholder; }} namespace std { template<> struct hash{ std::size_t operator()(xreate::versions::SymbolOrPlaceholder const& s) const; }; template<> struct equal_to{ bool operator()(const xreate::versions::SymbolOrPlaceholder& __x, const xreate::versions::SymbolOrPlaceholder& __y) const; }; } namespace xreate { namespace versions { enum PlaceholderFlag {SYMBOL, PLACEHOLDER}; struct SymbolOrPlaceholder { PlaceholderFlag flagEndOfLifePlaceholder; Symbol symbol; }; struct VersionImposedDependency{}; -/** \brief Represents results of Versions Analysis +/** + * \brief Represents the results of the versions analysis provided by \ref VersionsPass * \sa VersionsPass */ class VersionsGraph{ public: //processing API: void applyNatualDependencies(const Symbol& symbol, const std::list& dependencies); void applyDependentEndOfLife(const SymbolOrPlaceholder& symbol, const std::list& dependencies); void defineEndOfLife(const Symbol& symbol, const Symbol& symbolSuccessor); SymbolOrPlaceholder getEndOfLife(const Symbol& s); bool validate(); //examination API: AttachmentsContainerDefault>* representAsAttachments() const; void __debug_print(std::ostream& output) const; private: typedef std::unordered_map Path; std::unordered_multimap __inferiorsNatural; std::unordered_multimap __inferiors; std::unordered_map __dictSuccessors; std::list expandPlaceholder(const SymbolOrPlaceholder& symbol, const Symbol& symbolPrev) const; std::list extractCycle(const Path& path, const SymbolOrPlaceholder& symbolBeginning); bool tryEliminateEofAliases(const std::list& aliases); bool validateCycles(); bool validateCycles(const SymbolOrPlaceholder& s, std::unordered_multimap& graph, std::unordered_set& symbolsVisited, Path& path); }; -/** \brief Provides Versions Analysis and stores results in VersionsGraph */ +/** \brief Implements the versions analysis and stores the results in \ref VersionsGraph */ class VersionsPass: public AbstractPass> { typedef AbstractPass> Parent; public: VersionsPass(PassManager* manager): AbstractPass>(manager){} std::list process(const Expression& expression, PassContext context, const std::string& hintSymbol="") override; VersionsGraph& getResultGraph(); virtual void finish(); protected: std::list processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol="") override; private: VersionsGraph __graph; AttachmentsContainerDefault __symbolsVisited; }; }} //end of xreate::versions namespace xreate{ template<> std::list defaultValue>(); template<> struct AttachmentsDict { typedef std::list Data; static const unsigned int key = 8; }; } #endif /* VERSIONSPASS_H */ diff --git a/cpp/src/query/containers.cpp b/cpp/src/query/containers.cpp index a95fe9c..9bfa1a5 100644 --- a/cpp/src/query/containers.cpp +++ b/cpp/src/query/containers.cpp @@ -1,116 +1,111 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * containers.cpp * Created on 3/14/15. */ -/** - * \file query/containers.h - * \brief Represents reasoner's solution on [Container implementations](/w/concepts/containers) - */ - #include #include "query/containers.h" using namespace std; using namespace xreate::containers; using namespace xreate; Implementation Query::queryImplementation(xreate::Symbol const &s) { if (Attachments::exists(s)) { return Attachments::get(s); } return Implementation::create(s); } Query::Query() { Attachments::init(); } void Query::init(TranscendLayer* transcend) { if (flagDataIsLoaded) return; //Fill implementation data for a data sources: auto range = transcend->query(Config::get("containers.id.implementations")); if (range.size()) for(auto atom : range) { auto data = TranscendLayer::parse(atom.second); Symbol var = transcend->unpack(get<0>(data)); string implStr = get<1>(data).name().c_str(); if (implStr == Config::get("containers.impl.solid")) { auto size = TranscendLayer::parse(get<1>(data)); Attachments::put(var,{SOLID, ImplementationRec {get<0>(size)}}); } else if (implStr == Config::get("containers.impl.onthefly")) { Attachments::put(var,{ON_THE_FLY, ImplementationRec {var}}); } else { assert(false && "Unable to determine proper implementation for the symbol"); } } flagDataIsLoaded = true; } Implementation Implementation::create(const Symbol &var) { //TODO review implementation determination strategy Expression varDecl = CodeScope::getDefinition(var); switch (varDecl.op) { case Operator::LIST_RANGE: { ImplementationRec rec{var}; return {ON_THE_FLY, rec}; } case Operator::LIST: { return {SOLID, ImplementationRec {varDecl.getOperands().size()}}; } default: break; }; ImplementationLinkedList ill(var); if (ill) { return ill.getImplementationData(); } assert(false && "Unable to determine proper implementation for the symbol"); return Implementation(); } ImplementationLinkedList::ImplementationLinkedList(const Symbol& source) : flagIsValid(false), s(source) { const Expression& sourceExpr = CodeScope::getDefinition(source); if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))) { flagIsValid = true; Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist")); assert(tagLinkedlist.operands.size() == 2); fieldPointer = tagLinkedlist.operands.at(0).getValueString(); terminator = tagLinkedlist.operands.at(1); } } ImplementationLinkedList::operator bool () const { return flagIsValid; } Implementation ImplementationLinkedList::getImplementationData() const { return {ON_THE_FLY, ImplementationRec{s}}; } diff --git a/cpp/src/query/containers.h b/cpp/src/query/containers.h index ab8de85..e95cc7d 100644 --- a/cpp/src/query/containers.h +++ b/cpp/src/query/containers.h @@ -1,92 +1,96 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * containers.h * Created on 3/14/15. */ +/** + * \file query/containers.h + * \brief Transcend solutions on [Containers](/w/concepts/containers) implementation details + */ + #ifndef _XREATE_CONTAINERSQUERY_H_ #define _XREATE_CONTAINERSQUERY_H_ #include "xreatemanager.h" #include "transcendlayer.h" #include namespace xreate { namespace containers { enum ImplementationType {SOLID, ON_THE_FLY, LINKED_LIST}; template struct ImplementationRec; template<> struct ImplementationRec { size_t size; }; template<> struct ImplementationRec{ Symbol source; }; struct Implementation; struct ImplementationLinkedList { bool flagIsValid; std::string fieldPointer; Expression terminator; ImplementationLinkedList(const Symbol& source); operator bool() const; Implementation getImplementationData() const; private: Symbol s; }; struct Implementation { typedef boost::variant, ImplementationRec> Variant; ImplementationType impl; Variant data; static Implementation create(const Symbol &var); static Implementation create(const Symbol& var, const std::string &implSerialized); template const ImplementationRec& extract() const{ const ImplementationRec& rec = boost::get>(data); return rec; } }; - /** \brief Extracts solution about container implementation + /** + * \brief Queries Transcend solutions on containers implementation details * \sa xreate::containers::Iterator */ class Query : public xreate::IQuery { public: static Implementation queryImplementation(xreate::Symbol const &s); void init(TranscendLayer* transcend); Query(); ~Query(){} private: bool flagDataIsLoaded = false; PassManager *man; }; - - } template<> struct AttachmentsDict { typedef containers::Implementation Data; static const unsigned int key = 1; }; } #endif //_XREATE_CONTAINERSQUERY_H_ diff --git a/cpp/src/query/latex.h b/cpp/src/query/latex.h index 42e5d9e..c66f39f 100644 --- a/cpp/src/query/latex.h +++ b/cpp/src/query/latex.h @@ -1,46 +1,45 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** - * \file query/context.h - * \brief Represents reasoner's solution on [Context](/w/concepts/context) - * - * \class xreate::latex::LatexQuery + * \file query/latex.h + * \brief Transcend solution on [Context](/d/concepts/context/) implementation details */ #ifndef LATEXQUERY_H #define LATEXQUERY_H #include "transcendlayer.h" #include "aux/latereasoning.h" #include namespace xreate{ namespace latex{ typedef std::vector Demand; +/** \brief Queries Transcend solution on [Context](/d/concepts/context/) implementation details */ class LatexQuery: public IQuery{ public: VNameId LatexParametersOffset = 1000; //Default value. Overriden by `latex_parameters_offset` from transcend Demand getFnDemand(const std::string& fnName); latereasoning::LateAnnotation getDecision(const std::string& subject, const CodeScope* scopeCaller); std::list getSubjectDomain(const std::string& subject); void init(TranscendLayer* transcend); private: TranscendLayer* __transcend; std::map __demand; std::map, Gringo::Symbol> __decisions; std::map, latereasoning::LateAnnotation> __decisionsLate; std::map> __domains; }; } } #endif diff --git a/cpp/src/query/polymorph.h b/cpp/src/query/polymorph.h index f708fe6..7be4f56 100644 --- a/cpp/src/query/polymorph.h +++ b/cpp/src/query/polymorph.h @@ -1,35 +1,42 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: polymorph.h * Author: pgess * * Created on November 9, 2017, 12:14 PM */ +/** + * \file query/polymorph.h + * \brief Transcend solution on [Polymorphism](/d/concepts/polymorphism/) implementation details + */ + #ifndef POLYMORPHQUERY_H #define POLYMORPHQUERY_H #include "transcendlayer.h" #include "aux/latereasoning.h" #include namespace xreate { namespace polymorph { + +/** \brief Queries Transcend solution on [Polymorphism](/d/concepts/polymorphism/) implementation details */ class PolymorphQuery: public IQuery { public: latereasoning::LateAnnotation get(const Expression& e) const; Expression getValue(const Gringo::Symbol& s) const; virtual void init(TranscendLayer* transcend) override; private: std::unordered_map __cacheEarlyReasoning; std::unordered_map __cacheLateReasoning; TranscendLayer* __transcend = nullptr; }; }}//end of xreate::polymorph #endif /* POLYMORPHQUERY_H */ diff --git a/cpp/src/transcendlayer.cpp b/cpp/src/transcendlayer.cpp index adfda07..2ecd979 100644 --- a/cpp/src/transcendlayer.cpp +++ b/cpp/src/transcendlayer.cpp @@ -1,501 +1,495 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: transcendlayer.cpp */ /** * \file transcendlayer.h - * \brief Reasoner. Wrapper over the external Clasp reasoner library + * \brief Transcend reasoning implementation */ #include "transcendlayer.h" #include "analysis/utils.h" #include "utils.h" #include #include #include #include #include #include using namespace std; //TODO escape identifiers started with upper case symbol namespace xreate{ bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2) { return s1.id == s2.id && s1.flagIsUsed == s2.flagIsUsed; } struct VisitorSymbolNodeHash : public boost::static_visitor{ std::size_t operator()(const xreate::SymbolPacked& node) const noexcept { return 2 * (node.identifier + 3 * node.scope + 5 * std::abs(node.version)); } std::size_t operator()(const xreate::SymbolAnonymous& node) const noexcept { return 7 * node.id; } } ; } namespace std{ std::size_t hash::operator()(xreate::SymbolNode const& s) const noexcept { return boost::apply_visitor(xreate::VisitorSymbolNodeHash(), s); } std::size_t hash::operator()(xreate::SymbolGeneralized const& s) const noexcept { return xreate::AttachmentsId::getId(s); } } namespace xreate{ void TranscendLayer::printWarnings(std::ostream& out) { const std::string warningTag = "warning"; auto warningsModel = query(warningTag); if(warningsModel.size()) for(auto warning : warningsModel) { unsigned int warningId; Gringo::Symbol params; std::tie(warningId, params) = parse(warning.second); cout << "Warning: " << __warnings.at(warningId) << " "; params.print(out); out << params; } } bool TranscendLayer::processSolution(Gringo::Model const &model) { cout << "Model: " << endl; const string& atomBindVar = Config::get("transcend.bindings.variable"); const string& atomBindFunc = Config::get("transcend.bindings.function"); const string& atomBindScope = Config::get("transcend.bindings.scope"); for(Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) { atom.print(cout); cout << " | " << endl; string atomName(atom.name().c_str()); if(atomName == atomBindVar || atomName == atomBindFunc || atomName == atomBindScope) { string atomAlias = std::get<1>(parse(atom)).name().c_str(); __model.emplace(atomAlias, atom); continue; } __model.emplace(atomName, atom); } return true; } void TranscendLayer::registerReport(IAnalysisReport * report) { __reports.push_back(report); } void TranscendLayer::printReports() { for(IAnalysisReport* report : __reports) { report->print(__partGeneral); } } void TranscendLayer::deleteReports(){ for(IAnalysisReport* report : __reports) { delete report; } __reports.clear(); } void TranscendLayer::addRuleWarning(const RuleWarning & rule) { //__partGeneral << rule << endl; list domains; boost::format formatDef("%1%(%2%)"); std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(domains, domains.begin()), [&formatDef](const std::pair &argument) { string domain; switch(argument.second) { case DomainAnnotation::FUNCTION: domain = "function"; break; case DomainAnnotation::VARIABLE: domain = "variable"; break; } return boost::str(formatDef % domain % argument.first); }); list vars; std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(vars, vars.begin()), [](const std::pair &argument) { return argument.first.c_str(); }); list> guardsRaw; std::transform(rule.__guards.begin(), rule.__guards.end(), std::inserter(guardsRaw, guardsRaw.begin()), [this](const Expression & guard) { return xreate::analysis::compile(guard); }); const list& guards = xreate::analysis::multiplyLists(std::move(guardsRaw)); list &&branches = xreate::analysis::compileNeg(rule.__condition); boost::format formatWarning("warning(%1%, (%2%)):- %3%, %4%, %5%."); for(const string &guardsJoined : guards) for(const string &branch : branches) { unsigned int hook = registerWarning(string(rule.__message)); __partGeneral << formatWarning % (hook) % (boost::algorithm::join(vars, ", ")) % (branch) % (guardsJoined) % (boost::algorithm::join(domains, ", ")) << endl; } } unsigned int TranscendLayer::registerWarning(std::string && message) { static int warningId = 0; __warnings.emplace(warningId, message); return warningId++; } void TranscendLayer::involveImports() { ostream &out = __partGeneral; if(ast) for(string fn : ast->__rawImports) { std::ifstream file(fn); if(!file) { std::cout << "Can't process script file: " << fn << std::endl; assert(false); } while(!file.eof()) { string line; std::getline(file, line); out << line << endl; } } } void TranscendLayer::addRawScript(std::string && script) { __partGeneral << script; } void TranscendLayer::run() { involveImports(); printReports(); ostringstream program; program << __partTags.str() << __partGeneral.str(); cout << FYEL(program.str()) << endl; std::vector args{"clingo", nullptr}; DefaultGringoModule moduleDefault; Gringo::Scripts scriptsDefault(moduleDefault); ClingoLib ctl(scriptsDefault, 0, args.data(), { }, 0); ctl.add("base",{}, program.str()); ctl.ground({ {"base", {}} }, nullptr); // solve Gringo::SolveResult result = ctl.solve([this](Gringo::Model const &model) { this->processSolution(model); return true; }, { }); if(result.satisfiable() == Gringo::SolveResult::Satisfiable) { cout << FGRN("SUCCESSFULLY") << endl; } else { cout << FRED("UNSUCCESSFULLY") << endl; } // invoke all query plugins to process solution for(auto q : __queries) { q.second->init(this); } } TranscendLayer::TranscendLayer() : ast(nullptr) { } StaticModel TranscendLayer::query(const std::string & atom) const { StaticModel result; if (! __model.count(atom)) { return result; } auto currentDataRange = __model.equal_range(atom); std::copy(currentDataRange.first, currentDataRange.second, std::inserter(result, result.end())); return result; } ScopePacked TranscendLayer::pack(const CodeScope * const scope) { auto pos = __indexScopes.emplace(scope, __indexScopes.size()); if(pos.second) __registryScopes.push_back(scope); return pos.first->second; } size_t TranscendLayer::getScopesCount() const { return __registryScopes.size(); } SymbolPacked TranscendLayer::pack(const Symbol& symbol, std::string hintSymbolName) { SymbolPacked result(symbol.identifier.id, symbol.identifier.version, pack(symbol.scope)); __indexSymbolNameHints.emplace(result, hintSymbolName); return result; } Symbol TranscendLayer::unpack(const SymbolPacked & symbol) const { return Symbol{ScopedSymbol {symbol.identifier, symbol.version}, __registryScopes[symbol.scope]}; }; std::string TranscendLayer::getHintForPackedSymbol(const SymbolPacked & symbol) { auto result = __indexSymbolNameHints.find(symbol); return(result == __indexSymbolNameHints.end()) ? "" : result->second; } IQuery * TranscendLayer::registerQuery(IQuery *query, const QueryId & id) { return __queries.emplace(id, query).first->second; } IQuery * TranscendLayer::getQuery(const QueryId & id) { assert(__queries.count(id) && "Undefined query"); return __queries.at(id); } class VisitorUnpackSymbol : public boost::static_visitor{ public: VisitorUnpackSymbol(const TranscendLayer* transcend) : __transcend(transcend) { } SymbolGeneralized operator()(const SymbolPacked& symbol) const { return __transcend->unpack(symbol); } SymbolGeneralized operator()(const SymbolAnonymous& symbol) const { return symbol; } private: const TranscendLayer* __transcend; } ; class VisitorPackSymbol : public boost::static_visitor{ public: VisitorPackSymbol(TranscendLayer* transcend, const std::string& hintSymbolName) : __transcend(transcend), __hint(hintSymbolName) { } SymbolNode operator()(const Symbol& symbol) const { return __transcend->pack(symbol, __hint); } SymbolNode operator()(const SymbolAnonymous& symbol) const { return symbol; } private: TranscendLayer* __transcend; std::string __hint; } ; SymbolNode TranscendLayer::pack(const SymbolGeneralized& symbol, const std::string & hintSymbolName) { return boost::apply_visitor(VisitorPackSymbol(this, hintSymbolName), symbol); } SymbolGeneralized TranscendLayer::unpack(const SymbolNode & symbol) const { return boost::apply_visitor(VisitorUnpackSymbol(this), symbol); } bool operator==(const SymbolPacked& s1, const SymbolPacked & s2) { return s1.identifier == s2.identifier && s1.scope == s2.scope; } bool operator<(const SymbolPacked& s1, const SymbolPacked & s2) { return s1.scope < s2.scope || (s1.scope == s2.scope && s1.identifier < s2.identifier); } Expression ParseImplAtom ::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Num: return Expression(atom.num()); case Gringo::SymbolType::Str: return Expression(Atom(std::string(atom.string().c_str()))); case Gringo::SymbolType::Fun: { //FUNC Expression result(Operator::CALL,{Expression(Atom(std::string(atom.name().c_str())))}); for(const Gringo::Symbol& arg : atom.args()) { result.addArg(ParseImplAtom::get(arg)); } return result; } default: { assert(false); } } } int ParseImplAtom ::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Num: return atom.num(); default: break; } assert(false && "Inappropriate symbol type"); } std::string ParseImplAtom ::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Str: return atom.string().c_str(); case Gringo::SymbolType::Fun: return atom.name().c_str(); default: break; } assert(false && "Inappropriate symbol type"); } SymbolPacked ParseImplAtom ::get(const Gringo::Symbol & atom) { auto result = TranscendLayer::parse(atom); return SymbolPacked(std::get<0>(result), std::get<1>(result), std::get<2>(result)); }; Gringo::Symbol ParseImplAtom ::get(const Gringo::Symbol & atom) { return atom; } SymbolNode ParseImplAtom ::get(const Gringo::Symbol & atom) { assert(atom.type() == Gringo::SymbolType::Fun && "Inappropriate symbol type"); if(atom.name() == "a") { return SymbolAnonymous{(unsigned int) std::get<0>(TranscendLayer::parse(atom))}; } else if(atom.name() == "s") { return ParseImplAtom::get(atom); } assert(false && "Wrong symbol format"); } class VisitorSymbolId : public boost::static_visitor{ public: unsigned int operator()(const Symbol& symbol) const { return AttachmentsId::getId(symbol); } unsigned int operator()(const SymbolAnonymous& symbol) const { return symbol.id; } } ; unsigned int AttachmentsId ::getId(const SymbolGeneralized & symbol) { return boost::apply_visitor(VisitorSymbolId(), symbol); } } //end of xreate namespace /** * \class xreate::TranscendLayer - * \brief Reasoning and logic Solver. + * \brief Logic reasoning implementation. Internally, it's a proxy to the external ASP solver [Clasp](https://potassco.org/clasp/) * - * Wraps external brilliant fantastic tool [Clasp solver](https://potassco.org/clasp/) + * Performs reasoning over source codes in order to facilitate efficient compilation using results from a number of internal analyzers. + * Clients implement \ref IAnalysisReport to supply Transcend with data and implement \ref IQuery to find out resolutions. + * + * Transcend uses the following sources to build a logic program before actual reasoning is performed: + * - Raw content. Clients are free to include arbitrary ASP format data in the logic program. See \ref addRawScript(). + * - External scripts. External files with ASP scripts can be appended to the logic program. See `involveImports()` (private member). + * - Diagnostic rules to produce diagnostic messages during + * compilation(warnings) or even to signal to halt compilation with errors. See \ref addRuleWarning(), \ref registerWarning(). + * - Internal analyzers. The analyzer can publish logic facts and rules by implementing \ref IAnalysisReport interface. * - * For building *logic program* for reasoning TranscendLayer takes input from: - * - Raw scripts. Client could append arbitrary ASP script to _logic program_. \ref addRawScript() - * - Includes. There is possibility to specify external files with ASP scripts - * to append to _logic program_. \ref involveImports() (private member) - * - Diagnostic rules. Rules that produce diagnostic messages during - * compilation(warnings) or even able to halt compilation with errors. - * addRuleWarning(), \ref registerWarning() - * - DFA data. \ref setDFAData() - * - CFA data. \ref setCFAData() - * - Dominators Analysis. See xreate::dominators::DominatorsAnalysisProvider. - * Executed by \ref run() - * - Context rules. See xreate::ContextRule and general [Context Explanation](/w/concepts/context) + * Generally, Transcend input could be loosely broke down into three categories: + * - Internally derived data. CFA, DFA, and other analyzers automatically supply the reasoner with + * useful insights about source codes, the structure and algorithms of a program. + * - User provided custom data. Analyzers extract manual developer-provided annotations from the source codes. + * - External data. External files supply reasoner with third-party data + * which relates to different aspects of a program possibly produced by external analyzers. * - * Data sources implement xreate::IAnalysisReport. Generally, input could be loosely divided into three categories: - * - *Internally derived* data. CFA, DFA, Dominators analyses *automatically* feed reasoner by - * useful insights about data, structure and algorithms of a program - * - *User provided* data. CFA, DFA, Diagnostic/Context rules feed reasoner by - * annotations Developer specifically provides manually - * - *External* data. Raw scripts and includes feed reasoner with third-party data - * related to a different aspects of a program possibly produced by external analyzers + * After Transcend has gathered data from all providers and the logic program is fully constructed, + * it runs the external logic reasoner to receive back the solution. + * + * The solution from the external logic reasoner is accessible via *queries*. + * Classes which want to request data from Transcend should implement the \ref IQuery interface. See \ref IQuery descendants to find out currently available queries. * - * Once TranscendLayer got input from all providers and logic program is fully constructed - * it runs external Clasp reasoner and receives back desired solutions. - * - * Output of the external Clasp reasoner is recognized and accessed via *queries*. - * IQuery represents an interface between reasoner's output and rest of Xreate. - * Each query inherits xreate::IQuery interface. Currently there are queries as follows: - * - xreate::containers::Query to catch solutions regarding Containers implementation. See [Containers Explanation](/w/concepts/containers) - * - xreate::context::ContextQuery to catch solution regarding Context. See [Context Explanation](/w/concepts/context) - * - * \sa See xreate::dfa::DFAPass, xreate::cfa::CFAPass, xreate::IQuery, xreate::IAnalysisReport, xreate::dominators::DominatorsAnalysisProvider + * \section tl_adapt Adaptability + * Decorators are used to extend %TranscendLayer functionality. The default bundle defined by \ref DefaultTranscendLayerImpl. + * + * \sa See xreate::dfa::DFAPass, xreate::cfa::CFAPass, xreate::IQuery, xreate::IAnalysisReport */ diff --git a/cpp/src/transcendlayer.h b/cpp/src/transcendlayer.h index e0051bb..6ad56de 100644 --- a/cpp/src/transcendlayer.h +++ b/cpp/src/transcendlayer.h @@ -1,284 +1,284 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: transcendlayer.h */ #ifndef transcendLAYER_H #define transcendLAYER_H #include "ast.h" #include "contextrule.h" #include #include #include #include #include #include #include #include #include #include namespace xreate { typedef unsigned int ScopePacked; const ScopePacked SCOPE_ABSTRACT_GLOBAL = std::numeric_limits::max(); struct SymbolPacked { SymbolPacked(){} SymbolPacked(ScopedSymbol i, ScopePacked s): identifier(i.id), version(i.version), scope(s){} SymbolPacked(VNameId symbolId, versions::VariableVersion symbolVersion, ScopePacked symbolScope) : identifier(symbolId), version(symbolVersion), scope(symbolScope){} VNameId identifier; versions::VariableVersion version; ScopePacked scope; }; bool operator==(const SymbolPacked& s1, const SymbolPacked& s2); bool operator<(const SymbolPacked& s1, const SymbolPacked& s2); struct SymbolAnonymous { unsigned int id; bool flagIsUsed = false; }; bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2); typedef boost::variant SymbolNode; //DEBT use SymbolGeneralized communicating with Analysis rather than Symbol typedef boost::variant SymbolGeneralized; template<> struct AttachmentsId{ static unsigned int getId(const SymbolGeneralized& symbol); }; } namespace std { template<> struct hash { std::size_t operator()(xreate::SymbolNode const& s) const noexcept; }; template<> struct hash { std::size_t operator()(xreate::SymbolGeneralized const& s) const noexcept; }; } namespace xreate { enum class DFGConnection { STRONG, WEAK, PROTOTYPE }; -/** \brief Designated to mark analysis results that can be composed as *logic program* */ +/** \brief Supplies \ref TranscendLayer with results of an analysis*/ class IAnalysisReport { public: - /** \brief Composes *logic program* based on analysis data into ASP format and appends to a stream*/ + /** \brief Composes a logic program to represent the analysis data in ASP format, and appends to a stream*/ virtual void print(std::ostringstream& output) const = 0; virtual ~IAnalysisReport(){}; }; -/** \brief Logic program query interface */ +/** \brief Transcend solutions querying interface */ class IQuery { public: - virtual void init(TranscendLayer* transcend) = 0; - virtual ~IQuery() {} + virtual void init(TranscendLayer* transcend) = 0; + virtual ~IQuery() {} }; enum class QueryId { - ContainersQuery, - PolymorphQuery, - LatexQuery + ContainersQuery, + PolymorphQuery, + LatexQuery }; namespace dfa{ - class DFAGraph; + class DFAGraph; } namespace cfa { - class CFAGraph; + class CFAGraph; } typedef std::multimap StaticModel; typedef StaticModel::const_iterator StaticModelIterator; class TranscendLayer { friend class ContextRule; /**\name Data Providers Management */ ///@{ public: void registerReport(IAnalysisReport* report); void printReports(); void deleteReports(); - /** \brief Appends arbitrary string to *logic program* + /** \brief Appends arbitrary string to a logic program */ void addRawScript(std::string&& script); private: std::list __reports; /** Includes external text files to a *logic program* */ void involveImports(); ///@} /**\name Queries Management */ ///@{ public: - /** \brief Adds query. See xreate::IQuery */ + /** \brief Registers a query. See xreate::IQuery */ IQuery* registerQuery(IQuery* query, const QueryId& id); - /** \brief Returns particular query. See xreate::IQuery */ + /** \brief Returns a particular query. See xreate::IQuery */ IQuery* getQuery(const QueryId& id); template static std::tuple parse(const Gringo::Symbol& atom); StaticModel query(const std::string& atom) const; size_t getScopesCount() const; SymbolPacked pack(const Symbol& symbol, std::string hintSymbolName = ""); ScopePacked pack(const CodeScope * const scope); Symbol unpack(const SymbolPacked& symbol) const; SymbolNode pack(const SymbolGeneralized& symbol, const std::string& hintSymbolName); SymbolGeneralized unpack(const SymbolNode& symbol) const; std::string getHintForPackedSymbol(const SymbolPacked& symbol); ///@} private: std::map __queries; std::map __indexSymbolNameHints; std::unordered_map __indexScopes; std::vector __registryScopes; /**\name Diagnostic */ ///@{ //TODO diagnostic move over to separate provider/query public: - /** \brief Adds diagnostic rule */ + /** \brief Registers diagnostic rules */ void addRuleWarning(const RuleWarning &rule); /** \brief Registers diagnostic messages */ unsigned int registerWarning(std::string &&message); private: std::map __warnings; void printWarnings(std::ostream& out); ///@} ///@{ public: TranscendLayer(); /** \brief Executes reasoning */ void run(); ///@} AST *ast; protected: virtual bool processSolution(Gringo::Model const &model); private: StaticModel __model; std::ostringstream __partTags; std::ostringstream __partGeneral; }; template struct ParseImplAtom { static typ get(const Gringo::Symbol& atom) { return atom.num(); } }; template<> struct ParseImplAtom { static int get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static std::string get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static SymbolPacked get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static SymbolNode get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static Gringo::Symbol get(const Gringo::Symbol& atom); }; template struct ParseImplAtom>{ static std::list get(const Gringo::Symbol& atom){ bool flagIsList = (atom.type() == Gringo::SymbolType::Fun) && atom.name().empty(); std::list result; if(!flagIsList) { //treat as degenerate case: list with a single element result.push_back(ParseImplAtom::get(atom)); return result; } for (const Gringo::Symbol& arg: atom.args()) { result.push_back(ParseImplAtom::get(arg)); } return result; } }; template<> struct ParseImplAtom { static Expression get(const Gringo::Symbol& atom); }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { const size_t tupleSize = std::tuple_size::value; typedef typename std::tuple_element < tupleSize - index, Tuple>::type ElType; ElType& el = std::get < tupleSize - index > (tup); Gringo::Symbol atom = *arg; el = ParseImplAtom::get(atom); Parse_Impl ::parse(tup, ++arg); } }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { } }; template std::tuple TranscendLayer::parse(const Gringo::Symbol& atom) { typedef std::tuple < Types...> Tuple; Tuple tup; Parse_Impl::value>::parse(tup, atom.args().first); return tup; } } //end of xreate namespace #endif diff --git a/cpp/src/utils.cpp b/cpp/src/utils.cpp index b138bff..a901e38 100644 --- a/cpp/src/utils.cpp +++ b/cpp/src/utils.cpp @@ -1,38 +1,38 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * utils.cpp * * Author: pgess */ /** - * \file utils.h + * \file cpp/src/utils.h * \brief Auxiliary code */ #include "utils.h" #include using namespace xreate; Config Config::__self = Config(); Config::Config() : __storage{json_file{ "config/default.json" }} {} using boost::locale::conv::utf_to_utf; std::wstring utf8_to_wstring(const std::string& str) { return utf_to_utf(str.c_str(), str.c_str() + str.size()); } std::string wstring_to_utf8(const std::wstring& str) { return utf_to_utf(str.c_str(), str.c_str() + str.size()); } diff --git a/cpp/src/xreatemanager.cpp b/cpp/src/xreatemanager.cpp index fe81268..974bdc4 100644 --- a/cpp/src/xreatemanager.cpp +++ b/cpp/src/xreatemanager.cpp @@ -1,156 +1,151 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * xreatemanager.cpp * * Author: pgess * Created on July 3, 2017, 6:03 PM */ #include "xreatemanager.h" #include "pass/abstractpass.h" #include "aux/transcend-decorators.h" #include "aux/xreatemanager-decorators.h" #include "llvmlayer.h" #include #include namespace xreate { void PassManager::registerPass(IPass* pass, const PassId& id, IPass* parent) { __passes.emplace(id, pass); __passDependencies.emplace(parent, pass); } IPass* PassManager::getPassById(const PassId& id){ assert(__passes.count(id)); return __passes[id]; } bool PassManager::isPassRegistered(const PassId& id){ return __passes.count(id); } void PassManager::executePasses(){ std::list passes{nullptr}; while (passes.size()){ IPass* parent = passes.front(); auto range = __passDependencies.equal_range(parent); for (auto i=range.first; i!=range.second; ++i){ IPass* pass = i->second; pass->run(); pass->finish(); passes.push_back(pass); } passes.pop_front(); } } void PassManager::prepare(AST* ast){ root = ast; transcend = new DefaultTranscendLayerImpl(); transcend->ast = ast; llvm = new LLVMLayer(ast); } PassManager::~PassManager(){} typedef XreateManagerDecoratorFull XreateManagerDecoratorDefault; namespace details{ namespace tier2{ XreateManager* XreateManager::prepare(std::string&& code){ auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } }} namespace details { namespace tier1 { XreateManager* XreateManager::prepare(std::string&& code){ auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } }} -/** - * \class xreate::XreateManager - * \brief Entry point of Xreate API - * - * Manages whole Xreate's internal workflow. There are 4 distinctive stages covered by XreateManager: - * - initPasses() To init passes - * - executePasses() To execute passes - * - analyse() To run reasoner - * - run() To run compiler - * - * For adaptability manager comes with several *Frontends*: - * - xreate::details::tier2::XreateManager has all stages accessible by client for full control - * - xreate::details::tier1::XreateManager has only analyse() and run(), where analyse() combines execution of all previous stages - * - xreate::XreateManager has only run() to combine all stages for convenient use - * - * Moreover there are *Backends*: - * - xreate::XreateManagerDecoratorBase Simple backend intended for inheritance without much functionality - * - XreateManagerDecoratorFull Backend intended to initialize all builtin passes - * - * Thus, client's code could combine desired frontend and desired backend as see fit. - * Default xreate::XreateManager connects full backend to init all builtin passes - * to a simplest frontend with only run() available to execute all stages at once - */ - -/** - *\brief Constructs XreateManager for a given code - */ XreateManager* XreateManager::prepare(std::string&& code) { auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } -/** - *\brief Constructs XreateManager for a given script file - */ XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } } + +/** + * \class xreate::XreateManager + * \brief Entry point of Xreate API + * + * Manages whole Xreate compiler's internal workflow. There are 4 distinctive stages managed by XreateManager: + * - `initPasses()` To init passes. + * - `executePasses()` To execute passes. + * - `analyse()` To run the reasoner. + * - `run()` To run the compiler. + * + * * \section xm_adapt Adaptability + * For adaptability reasons XreateManager comes with several *frontends*: + * - `xreate::details::tier2::XreateManager` exposes all the stages to clients for full control. + * - `xreate::details::tier1::XreateManager` exposes `analyse()` along with `run()`, where `analyse()` combines execution of all previous stages. + * - `xreate::XreateManager` exposes `run()` only which properly initializes and executes all the stages for convenient use. + * + * Besides, there are also *backends* as follows: + * - \ref XreateManagerDecoratorBase Simple backend intended for inheritance, does not provide much functionality. + * - \ref XreateManagerDecoratorFull Backend intended to initialize all builtin passes. + * + * Thus, client could combine a desired frontend along with a desired backend as it sees fit. + * Default xreate::XreateManager wraps around the full backend to init all builtin passes, and + * the simplest frontend with the only `run()` exposed to execute all stages at once. + */ \ No newline at end of file diff --git a/cpp/src/xreatemanager.h b/cpp/src/xreatemanager.h index 19919de..1d2f8ad 100644 --- a/cpp/src/xreatemanager.h +++ b/cpp/src/xreatemanager.h @@ -1,141 +1,145 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * xreatemanager.h * * Author: pgess * Created on July 3, 2017, 6:03 PM */ /** * \file - * \brief Entry point of Xreate API. - * + * \brief The entry point of Xreate API. */ #ifndef PASSMANAGER_H #define PASSMANAGER_H #include #include //stdio external struct _IO_FILE; typedef struct _IO_FILE FILE; namespace xreate { namespace grammar { namespace main { class Scanner; }}} namespace xreate { class IPass; class TranscendLayer; class LLVMLayer; class AST; enum class PassId { CFAPass, CFATemporalSeqPass, CompilePass, DFAPass, EnvironmentTestsPass, LoggerPass, RulesPass, InterpretationPass, VersionsPass }; /** * \class PassManager - * \brief Base class to control passes + * \brief The base class to control passes */ class PassManager{ public: void prepare(AST* ast); void registerPass(IPass* pass, const PassId& id, IPass* prerequisite=nullptr); IPass* getPassById(const PassId& id); bool isPassRegistered(const PassId& id); void executePasses(); virtual ~PassManager(); TranscendLayer* transcend; LLVMLayer* llvm; AST* root; private: std::map __passes; std::multimap __passDependencies; }; namespace details{ namespace tier2{ class XreateManager: public virtual PassManager{ public: virtual ~XreateManager(){}; virtual void initPasses()=0; // virtual void executePasses()=0; virtual void analyse()=0; virtual void* run()=0; static XreateManager* prepare(std::string&& code); static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public Decorator { public: }; }} //namespace details::tier2 namespace details{ namespace tier1{ class XreateManager: public virtual PassManager{ public: virtual void analyse()=0; virtual void* run()=0; static XreateManager* prepare(std::string&& code); static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public XreateManager, public details::tier2::XreateManagerImpl { typedef details::tier2::XreateManagerImpl PARENT; public: void analyse(){ PARENT::initPasses(); PARENT::executePasses(); PARENT::analyse(); } void* run(){ return PARENT::run(); } }; }} //namespace details::tier1 class XreateManager: public virtual PassManager{ public: + + /** \brief Consequently executes all compilation and code execution phases */ virtual void* run()=0; + /** \brief Constructs XreateManager for a given code */ static XreateManager* prepare(std::string&& code); + + /** \brief Constructs XreateManager for a given script file */ static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public XreateManager, public details::tier1::XreateManagerImpl{ typedef details::tier1::XreateManagerImpl PARENT; public: void* run(){ PARENT::analyse(); return PARENT::run(); } }; } //namespace xreate #endif diff --git a/cpp/tests/unit-test-example.cpp b/cpp/tests/unit-test-example.cpp new file mode 100644 index 0000000..17151bb --- /dev/null +++ b/cpp/tests/unit-test-example.cpp @@ -0,0 +1,43 @@ +#include "xreatemanager.h" //main Xreate header +#include "transcendlayer.h" +#include + +using namespace xreate; +using namespace std; + +TEST(Example, Example1){ + //Your custom transcend rules if any + string rules = + R"SCRIPT( + bind_func(sum, entry). + )SCRIPT"; + + //Your custom program + string example = + R"CODE( + //Custom code + + sum = function(a:: int, b:: int):: int + { + a + b + } + )CODE"; + + //Initialize compiler + unique_ptr man(XreateManager::prepare(move(example))); + + //Add transcend part: + man->transcend->addRawScript(move(rules)); + + //Define signature of your entry function: + typedef int (*ENTRYFN)(int, int); + + //Compile the example and get a pointer to the entry function: + ENTRYFN yourEntryFn = (ENTRYFN) man->run(); + + //Now execute function and check the result + int resultActual = yourEntryFn(5, 7); + int resultExpected = 5 + 7; + + ASSERT_EQ(resultExpected, resultActual); +} diff --git a/documentation-api/XreateDoxyfile b/documentation-api/XreateDoxyfile index b6264fb..512fdab 100644 --- a/documentation-api/XreateDoxyfile +++ b/documentation-api/XreateDoxyfile @@ -1,2406 +1,2406 @@ # Doxyfile 1.8.11 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the config file # that follow. The default is UTF-8 which is also the encoding used for all text # before the first occurrence of this tag. Doxygen uses libiconv (or the iconv # built into libc) for the transcoding. See http://www.gnu.org/software/libiconv # for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "Xreate" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = "Xreate programming language" +PROJECT_BRIEF = "The Xreate programming language" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = "/tmp/xreate-documentation-api" +OUTPUT_DIRECTORY = "tmp-generated-doc-api" # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = ../ # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines. ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # C#, C, C++, D, PHP, Objective-C, Python, Fortran (fixed format Fortran: # FortranFixed, free formatted Fortran: FortranFree, unknown formatted Fortran: # Fortran. In the later case the parser tries to guess whether the code is fixed # or free formatted code, this is the default for Fortran type files), VHDL. For # instance to make doxygen treat .inc files as Fortran files (default is PHP), # and .f files as C (default is Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See http://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = YES # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # http://www.riverbankcomputing.co.uk/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = YES # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = YES # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = YES # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = YES # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # and Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = YES # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = XreateDoxygenLayout.xml # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also http://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. -QUIET = NO +QUIET = YES # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = ../cpp/src ./ # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: http://www.gnu.org/software/libiconv) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox, *.py, *.pyw, *.f90, *.f, *.for, *.tcl, # *.vhd, *.vhdl, *.ucf, *.qsf, *.as and *.js. FILE_PATTERNS = *.cpp *.h *.dox # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = YES # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # function all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see http://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the config file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = NO #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # http://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: http://developer.apple.com/tools/xcode/), introduced with # OSX 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html # for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: http://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: http://qt-project.org/doc/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # http://qt-project.org/doc/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # http://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from http://www.mathjax.org before deployment. # The default value is: http://cdn.mathjax.org/mathjax/latest. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /