diff --git a/cpp/src/ExternLayer.cpp b/cpp/src/ExternLayer.cpp index 949d23a..6ede88c 100644 --- a/cpp/src/ExternLayer.cpp +++ b/cpp/src/ExternLayer.cpp @@ -1,321 +1,325 @@ /* 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 + */ // // Created by pgess on . // #include "ExternLayer.h" #include #include #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/Lex/PreprocessorOptions.h" #include #include #include #include "../../vendors/clang-codegen-private-3.8/CodeGenModule.h" -using namespace xreate; 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_NAMED); //TODO ?? implement Expression parsing(Array of Expr as vector); for(size_t i=0, size=e.operands.size(); i 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 "<__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/lib/llvm-3.6/lib/clang/3.6.2/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); __cgo.reset(new CodeGenOptions); __llvm->module->setDataLayout(ast->getASTContext().getTargetInfo().getDataLayoutString()); std::unique_ptr __hso(new HeaderSearchOptions()); std::unique_ptr __ppo(new PreprocessorOptions()); __cgm.reset(new CodeGen::CodeGenModule( ast->getASTContext(), *__hso, *__ppo, *__cgo, *__llvm->module, ast->getASTContext().getDiagnostics())); }; 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 __cgm->getTypes().ConvertType(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; } 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: "<getTypes().ConvertType(tyFuncQual); llvm::FunctionType* tyRawFunc = llvm::dyn_cast(tyRaw); llvm::Function* function = llvm::Function::Create(tyRawFunc, llvm::GlobalValue::ExternalLinkage, name, __llvm->module); __functions.emplace(name, function); return function; } +}//end of xreate namespace diff --git a/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp b/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp index 463c1f7..22111cd 100644 --- a/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp +++ b/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp @@ -1,236 +1,241 @@ /* 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: DominatorsTreeAnalysisProvider.cpp * Author: pgess * * Created on May 13, 2016, 11:39 AM */ +/** + * \file DominatorsTreeAnalysisProvider.h + * \brief Dominators Tree analysis + */ + #include "analysis/cfagraph.h" #include "analysis/DominatorsTreeAnalysisProvider.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/Support/GenericDomTreeConstruction.h" #include "llvm/Support/GenericDomTree.h" #include #include #include using namespace std; using namespace xreate; using namespace boost; using namespace boost::bimaps; namespace xreate{ namespace dominators { struct ControlFlowTree; struct Node { ScopePacked scope; ControlFlowTree* tree; }; /* bool operator !=(const Node& a, const Node& b){ return (a.tree != b.tree) || (a.scope != b.scope); } Node& operator++(Node& a){ ++a.scope; return a; } */ struct ControlFlowTree{ typedef bimap, multiset_of> CHILD_RELATIONS; CHILD_RELATIONS edges; std::vector nodes; Node* entry = nullptr; size_t size; ControlFlowTree(const size_t nodesCount): nodes(nodesCount), size(nodesCount){ } static ControlFlowTree* build(const ClaspLayer* engine){ ControlFlowTree* tree = new ControlFlowTree(engine->getScopesCount()); cfa::CFAGraph* graph = engine->dataCFA.get(); for (const auto& edge: graph->__parentScopeRelations){ tree->edges.insert(CHILD_RELATIONS::value_type(edge.second, edge.first)); } for (const auto& edge: graph->__callRelations){ unsigned int calleeFunction = edge.right; ScopePacked caller = edge.left; auto range = graph->__parentFunctionRelations.right.equal_range(calleeFunction); for (auto& i=range.first; i!= range.second; ++i){ tree->edges.insert(CHILD_RELATIONS::value_type(caller, i->second)); } } for (size_t i=0; isize; ++i){ tree->nodes[i]= Node{(unsigned int) i, tree}; } return tree; } std::list getRootFunctions() const{ size_t idMax = size; size_t id =0; std::list results; auto i = edges.right.begin(); while (id < idMax) { if (i!= edges.right.end() && i->first == id){ i = edges.right.upper_bound(i->first); } else { results.push_back(id); } ++id; } return std::move(results); } }; }} //end of namespace xreate::dominators namespace llvm { using namespace xreate::dominators; - + template <> struct GraphTraits { typedef Node* nodes_iterator; typedef Node NodeType; typedef std::function Transformer; typedef typename boost::transform_iterator ChildIteratorType; static ChildIteratorType child_begin(const nodes_iterator& node) { auto range = node->tree->edges.left.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.first, x); } static ChildIteratorType child_end(const nodes_iterator& node) { auto range = node->tree->edges.left.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.second, x); } }; template <> struct GraphTraits> { typedef Node* nodes_iterator; typedef Node NodeType; typedef std::function Transformer; typedef typename boost::transform_iterator ChildIteratorType; static ChildIteratorType child_begin(const nodes_iterator& node) { auto range = node->tree->edges.right.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.first, x); } static ChildIteratorType child_end(const nodes_iterator& node) { auto range = node->tree->edges.right.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.second, x); } }; template <> struct GraphTraits: public GraphTraits { static NodeType* getEntryNode(ControlFlowTree* F) { if (F->entry) return F->entry; list&& roots = F->getRootFunctions(); assert(roots.size()==1); return F->entry = &F->nodes[roots.front()]; } static nodes_iterator nodes_begin(ControlFlowTree* F) { return &F->nodes[0]; } static nodes_iterator nodes_end(ControlFlowTree* F) { return &F->nodes[F->size]; } static size_t size(ControlFlowTree* F) { return F->size; } }; } namespace xreate{ namespace dominators { class DominatorTree: public llvm::DominatorTreeBase { public: DominatorsTreeAnalysisProvider::Dominators dominators; DominatorTree(bool isPostDom): llvm::DominatorTreeBase(isPostDom) {} void run(ControlFlowTree& program){ recalculate(program); //extract dominators info for (auto& entry: DomTreeNodes){ if (!entry.getFirst()) continue; dominators.emplace(entry.getFirst()->scope, make_pair(entry.getSecond()->getDFSNumIn(), entry.getSecond()->getDFSNumOut())); } } void print(std::ostringstream& output, const std::string& atom) const { boost::format formatAtom(atom + "(%1%, range(%2%, %3%))."); for (auto entry: dominators){ output << formatAtom % (entry.first) % (entry.second.first) % (entry.second.second) << endl; } } }; void DominatorsTreeAnalysisProvider::run(const ClaspLayer* engine){ boost::scoped_ptr program(ControlFlowTree::build(engine)); treeForwardDominators->run(*program); treePostDominators->run(*program); } void DominatorsTreeAnalysisProvider::print(std::ostringstream& output) const{ treeForwardDominators->print(output, "cfa_forwdom"); treePostDominators->print(output, "cfa_postdom"); } const DominatorsTreeAnalysisProvider::Dominators& DominatorsTreeAnalysisProvider::getForwardDominators() const{ return treeForwardDominators->dominators; } const DominatorsTreeAnalysisProvider::Dominators& DominatorsTreeAnalysisProvider::getPostDominators() const{ return treePostDominators->dominators; } DominatorsTreeAnalysisProvider::DominatorsTreeAnalysisProvider() : treeForwardDominators(new DominatorTree(false)) , treePostDominators(new DominatorTree(true)) {} DominatorsTreeAnalysisProvider::~DominatorsTreeAnalysisProvider() {} }} //end of namespace xreate::dominators //void //CodeScopesTree::print(){ // typedef llvm::GraphTraits Traits; // for (size_t i=0; i" << (*j)->scope << endl; // } // } //} diff --git a/cpp/src/analysis/DominatorsTreeAnalysisProvider.h b/cpp/src/analysis/DominatorsTreeAnalysisProvider.h index 3a087a7..5363c6d 100644 --- a/cpp/src/analysis/DominatorsTreeAnalysisProvider.h +++ b/cpp/src/analysis/DominatorsTreeAnalysisProvider.h @@ -1,41 +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: DominatorsTreeAnalysisProvider.h * Author: pgess * * Created on May 13, 2016, 11:39 AM */ #ifndef DOMINATORSTREEANALYSISPROVIDER_H #define DOMINATORSTREEANALYSISPROVIDER_H #include "clasplayer.h" #include namespace xreate{namespace dominators{ class DominatorTree; - class DominatorsTreeAnalysisProvider: public IAnalysisData { + /** \brief Dominators Analysis report */ + class DominatorsTreeAnalysisProvider: public IAnalysisReport { public: typedef std::pair DominatedRange; typedef std::map Dominators; DominatorsTreeAnalysisProvider(); virtual ~DominatorsTreeAnalysisProvider(); void run(const ClaspLayer* engine); void print(std::ostringstream& output) const; const Dominators& getForwardDominators() const; const Dominators& getPostDominators() const; private: boost::scoped_ptr treeForwardDominators; boost::scoped_ptr treePostDominators; }; }} //end of namespace xreate::dominators #endif /* DOMINATORSTREEANALYSISPROVIDER_H */ diff --git a/cpp/src/analysis/aux.cpp b/cpp/src/analysis/aux.cpp index 0b9dadb..e7e2e1f 100644 --- a/cpp/src/analysis/aux.cpp +++ b/cpp/src/analysis/aux.cpp @@ -1,150 +1,155 @@ /* 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 + */ + #include "aux.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; } std::list compile(const Expression &e){ list result; switch (e.op) { case Operator::CALL: { assert(e.__state == Expression::COMPOUND); 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::NONE: { 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; } //TODO Null ad hoc ClaspLayer implementation // if (e.isNone()){ // result.push_back(e.__valueS); // } assert(result.size()); 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; } boost::format formatSymbol(const SymbolPacked& s){ boost::format formatSymbNamed("(%1%, %2%, %3%)"); boost::format formatSymbAnonymous("anonym(%1%, %2%)"); if (!s.categoryTransient){ return formatSymbNamed % s.identifier % s.version % s.scope; } else { return formatSymbAnonymous % s.identifier % s.scope; } } }} diff --git a/cpp/src/analysis/cfagraph.cpp b/cpp/src/analysis/cfagraph.cpp index 5f896ab..cd37e58 100644 --- a/cpp/src/analysis/cfagraph.cpp +++ b/cpp/src/analysis/cfagraph.cpp @@ -1,169 +1,175 @@ /* 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 + * + */ + #include "analysis/cfagraph.h" #include "analysis/aux.h" using namespace xreate::cfa; using namespace std; void CFAGraph::print(std::ostringstream& output) const { const std::string& atomBinding = Config::get("clasp.bindings.function"); const std::string& atomBindingScope = Config::get("clasp.bindings.scope"); //show function tags int counterTags = 0; std::ostringstream bufFunctionNames; boost::format formatFunction("function(%1%)."); boost::format formatBind(atomBinding + "(%1%, %2%)."); for (auto function: this->__nodesFunction.left) { const auto tags = this->__functionTags.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 functtion tags at all" << endl; } //declare scopes boost::format formatScope("scope(0..%1%)."); output << formatScope % (__clasp->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; } output << endl << "%\t\tStatic analysis: CFA" << endl; //parent connections //TOTEST CFG parent function boost::format formatFunctionParent("cfa_parent(%1%, function(%2%))."); for (const auto &relation: this->__parentFunctionRelations) { const string& function = this->__nodesFunction.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->__parentScopeRelations) { 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->__nodesFunction.left.at(relation.right); output << formatCall % (scopeFrom) % (functionTo) << endl; } //function specializations descrtiption //SECTIONTAG late-context cfa_function_specializations boost::format formatSpecializations("cfa_function_specializations(%1%, %2%)."); const list& functions = __clasp->ast->getAllFunctions(); for (auto f: functions){ if (f->guardContext.isValid()){ list guardRaw = xreate::analysis::compile(f->guardContext); assert(guardRaw.size() == 1); output << formatSpecializations % (f->getName()) % (guardRaw.front()) << endl; } } } void CFAGraph::addFunctionAnnotations(const std::string& function, const std::map& tags) { unsigned int fid = registerNodeFunction(function); for (auto& tag: tags){ __functionTags.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& scopeFrom, const std::string& functionTo) { unsigned int idFuncTo = registerNodeFunction(functionTo); __callRelations.insert(CALL_RELATIONS::value_type(scopeFrom, idFuncTo)); } void CFAGraph::addParentConnection(const ScopePacked& scope, const std::string& functionParent){ __parentFunctionRelations.insert(PARENT_FUNCTION_RELATIONS::value_type(scope, registerNodeFunction(functionParent))); } void CFAGraph::addParentConnection(const ScopePacked& scope, const ScopePacked& scopeParent){ __parentScopeRelations.emplace(scope, scopeParent); } unsigned int CFAGraph::registerNodeFunction(const std::string& fname){ auto pos = __nodesFunction.left.insert(make_pair(__nodesFunction.size(), fname)); return pos.first->first; } diff --git a/cpp/src/analysis/cfagraph.h b/cpp/src/analysis/cfagraph.h index 9a5bfd8..5219525 100644 --- a/cpp/src/analysis/cfagraph.h +++ b/cpp/src/analysis/cfagraph.h @@ -1,59 +1,60 @@ /* 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 "clasplayer.h" namespace xreate {namespace cfa { - - class CFAGraph: public IAnalysisData { + + /** \brief Represents CFA analysis data produced by CFAPass */ + class CFAGraph: public IAnalysisReport { public: typedef boost::bimap> PARENT_FUNCTION_RELATIONS; PARENT_FUNCTION_RELATIONS __parentFunctionRelations; std::map __parentScopeRelations; typedef boost::bimap< boost::bimaps::multiset_of, boost::bimaps::multiset_of, boost::bimaps::set_of_relation<> > CALL_RELATIONS; CALL_RELATIONS __callRelations; boost::bimap __nodesFunction; std::multimap __functionTags; std::multimap __scopeTags; std::multimap __contextRules; void print(std::ostringstream& output) const; CFAGraph(ClaspLayer* engine): __clasp(engine){} void addFunctionAnnotations(const std::string& function, 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& scopeFrom, const std::string& functionTo); void addParentConnection(const ScopePacked& scope, const std::string& functionParent); void addParentConnection(const ScopePacked& scope, const ScopePacked& scopeParent); // void addScopeRetIdentifier(const ScopePacked& scope, const SymbolPacked& identifier); private: ClaspLayer* __clasp; 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 8863259..38a9601 100644 --- a/cpp/src/analysis/dfagraph.cpp +++ b/cpp/src/analysis/dfagraph.cpp @@ -1,254 +1,260 @@ /* 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 + * + */ + #include "analysis/dfagraph.h" #include "analysis/aux.h" #include using namespace std; namespace xreate { namespace dfa { void DFAGraph::print(std::ostringstream& output) const { std::set symbols; output << endl << "%\t\tStatic analysis: DFA" << endl; std::vector>::const_iterator i1; std::vector::const_iterator i2; boost::format formatDfaConnection("dfa_connection(%1%, %2%, %3%)."); for (i1 = this->__edges.begin(), i2 = this->__data.begin(); i1 != this->__edges.end(); ++i1, ++i2) { string edgeName; switch (*i2) { case DFGConnection::WEAK: edgeName = "weak"; break; case DFGConnection::STRONG: edgeName = "strong"; break; case DFGConnection::PROTOTYPE: edgeName = "proto"; break; } output << formatDfaConnection % analysis::formatSymbol(i1->first) % analysis::formatSymbol(i1->second) % edgeName << " %" << this->__clasp->getHintForPackedSymbol(i1->first) << " - " << this->__clasp->getHintForPackedSymbol(i1->second) << endl; symbols.insert(i1->first); symbols.insert(i1->second); } boost::format formatDfaDependency("dfa_dependency(%1%, %2%)."); for (auto i = this->__dependencies.begin(); i != this->__dependencies.end(); ++i) { output << formatDfaDependency % analysis::formatSymbol(i->first) % analysis::formatSymbol(i->second) << " %" << this->__clasp->getHintForPackedSymbol(i->first) << " - " << this->__clasp->getHintForPackedSymbol(i->second) << endl; } boost::format formatBind("bind(%1%, %2%)."); for (const pair& tag : this->__tags) { for (string variant : xreate::analysis::compile(tag.second)) { output << formatBind % analysis::formatSymbol(tag.first) % (variant) << "%" << this->__clasp->getHintForPackedSymbol(tag.first) << endl; } symbols.insert(tag.first); } for (const SymbolPacked& s : symbols) { output << "v(" << analysis::formatSymbol(s) << ")." << " %" << this->__clasp->getHintForPackedSymbol(s) << endl; } } class VisitorAddTag : public boost::static_visitor<> { public: void operator()(const SymbolPacked& symbol) { __graph->__tags.emplace(symbol, move(__tag)); } void operator()(SymbolAnonymous& symbol) { symbol.tags.push_back(move(__tag)); } void operator()(const SymbolInvalid& symbol) { assert(false && "Undefined behaviour"); } VisitorAddTag(DFAGraph * const dfagraph, Expression&& tag) : __graph(dfagraph), __tag(tag) { } private: DFAGraph * const __graph; Expression __tag; }; class VisitorAddLink : public boost::static_visitor<> { public: void operator()(const SymbolPacked& nodeFrom) { if (!__graph->isConnected(__nodeTo, nodeFrom)) { __graph->__edges.emplace_back(__nodeTo, nodeFrom); __graph->__data.push_back(__link); DFAGraph::EdgeId eid = __graph->__edges.size() - 1; __graph->__outEdges.emplace(nodeFrom, eid); } } void operator()(const SymbolAnonymous& symbolFrom) { switch (__link) { case DFGConnection::WEAK: { //virtual symbol to hold transient annotations SymbolPacked symbPivot = __graph->createAnonymousSymbol(symbolFrom.scope); __graph->addConnection(symbPivot, symbolFrom, DFGConnection::STRONG); __graph->addConnection(__nodeTo, symbPivot, DFGConnection::WEAK); break; } case DFGConnection::STRONG: { for (const Expression& tag : symbolFrom.tags) { __graph->__tags.emplace(__nodeTo, tag); } break; } default: assert(false && "Undefined behavior"); } } void operator()(const SymbolInvalid&) { if (__link == DFGConnection::STRONG) return; if (__link == DFGConnection::WEAK) return; assert(false && "Undefined behavior"); } VisitorAddLink(DFAGraph * const dfagraph, const SymbolPacked& nodeTo, DFGConnection link) : __graph(dfagraph), __nodeTo(nodeTo), __link(link) { } private: DFAGraph * const __graph; SymbolPacked __nodeTo; DFGConnection __link; }; class VisitorGetDependencyConnection : public boost::static_visitor> { public: list operator()(const SymbolPacked & nodeFrom) { return { nodeFrom }; } list operator()(const SymbolAnonymous & nodeFrom) { return nodeFrom.dependencies; } list operator()(const SymbolInvalid&) { assert(false && "Undefined behavior"); } VisitorGetDependencyConnection(DFAGraph * const g) : graph(g) { } DFAGraph * const graph; }; class VisitorSetDependencyConnection : public boost::static_visitor<> { public: void operator()(SymbolPacked& nodeTo) { VisitorGetDependencyConnection visitorGetDepenencies(graph); auto deps = boost::apply_visitor(visitorGetDepenencies, nodeFrom); for (const SymbolPacked& dep : deps) { graph->__dependencies.emplace(nodeTo, dep); } } void operator()(SymbolAnonymous& nodeTo) { VisitorGetDependencyConnection visitorGetDepenencies(graph); auto deps = boost::apply_visitor(visitorGetDepenencies, nodeFrom); for (const SymbolPacked& dep : deps) { nodeTo.dependencies.push_back(dep); } } void operator()(SymbolInvalid&) { assert(false && "Undefined behavior"); } VisitorSetDependencyConnection(DFAGraph * const g, SymbolNode s) : graph(g), nodeFrom(s) { } DFAGraph * const graph; SymbolNode nodeFrom; }; bool DFAGraph::isConnected(const SymbolPacked& identifierTo, const SymbolPacked& identifierFrom) { auto range = __outEdges.equal_range(identifierFrom); for (std::multimap::iterator edge = range.first; edge != range.second; ++edge) { if (__edges[edge->second].second == identifierTo) return true; } return false; } void DFAGraph::addConnection(const SymbolPacked& nodeTo, const SymbolNode& nodeFrom, DFGConnection link) { VisitorAddLink visitor(this, nodeTo, link); boost::apply_visitor(visitor, nodeFrom); } void DFAGraph::addDependencyConnection(SymbolNode& identifierTo, SymbolNode& identifierFrom) { VisitorSetDependencyConnection visitor(this, identifierFrom); boost::apply_visitor(visitor, identifierTo); } void DFAGraph::addAnnotation(SymbolNode& node, Expression&& tag) { VisitorAddTag visitor(this, move(tag)); boost::apply_visitor(visitor, node); } SymbolPacked DFAGraph::createAnonymousSymbol(const ScopePacked& scope) { return SymbolPacked(ScopedSymbol{__countAnonymousSymbols++, 0}, scope, true); } }} //end of namespace xreate::dfa diff --git a/cpp/src/analysis/dfagraph.h b/cpp/src/analysis/dfagraph.h index 9ba5af0..531151e 100644 --- a/cpp/src/analysis/dfagraph.h +++ b/cpp/src/analysis/dfagraph.h @@ -1,63 +1,64 @@ /* 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 "clasplayer.h" namespace xreate {namespace dfa { struct SymbolAnonymous { SymbolAnonymous(unsigned int symbolId): id(symbolId){} unsigned int id; std::list tags; ScopePacked scope; std::list dependencies; }; struct SymbolInvalid { }; typedef boost::variant SymbolNode; - class DFAGraph: public IAnalysisData{ + /** \brief Holds DFA Analysis report produced by DFAPass */ + class DFAGraph: public IAnalysisReport{ friend class VisitorAddTag; friend class VisitorAddLink; friend class VisitorGetDependencyConnection; friend class VisitorSetDependencyConnection; public: DFAGraph(ClaspLayer* engine): __clasp(engine){} SymbolPacked createAnonymousSymbol(const ScopePacked& scope); void addAnnotation(SymbolNode& identifier, Expression&& tag); void addConnection(const SymbolPacked& identifierTo, const SymbolNode& identifierFrom, DFGConnection link); void addDependencyConnection(SymbolNode& identifierTo, SymbolNode& identifierFrom); bool isConnected(const SymbolPacked& identifierTo, const SymbolPacked& identifierFrom); void print(std::ostringstream& output) const; private: typedef unsigned int EdgeId; std::vector> __edges; std::multimap __outEdges; std::vector __data; std::multimap __tags; std::multimap __dependencies; unsigned int __countAnonymousSymbols=0; ClaspLayer* __clasp; }; }} // end of namespace xreate::dfa #endif /* DFA_H */ diff --git a/cpp/src/analysis/typeinference.cpp b/cpp/src/analysis/typeinference.cpp index 26e098a..3107a24 100644 --- a/cpp/src/analysis/typeinference.cpp +++ b/cpp/src/analysis/typeinference.cpp @@ -1,63 +1,68 @@ /* 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 "llvm/IR/Function.h" #include "llvm/IR/DerivedTypes.h" 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/dissalow 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); } return source; } 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::getDeclaration(s), ast); + return getType(CodeScope::getDefinition(s), ast); } assert(false && "Type can't be determined for an expression"); } } } //end of namespace xreate::typeinference diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp index 3fc3cf2..f45a2dc 100644 --- a/cpp/src/ast.cpp +++ b/cpp/src/ast.cpp @@ -1,964 +1,969 @@ /* 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; } 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::ARRAY: { assert(t.__operands.size() == 1); Expanded elTy = expandType(t.__operands.at(0)); return ExpandedType(TypeAnnotation(tag_array, elTy, 0)); } case TypeOperator::STRUCT: { assert(t.__operands.size()); std::vector&& packOperands = expandOperands(t.__operands); auto typNew = TypeAnnotation(TypeOperator::STRUCT, 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: { std::string alias = t.__valueCustom; /* if (signatures.count(alias)) { return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t})); } signatures[alias].emplace(t); */ //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::STRUCT); 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::VARIANT: { return ExpandedType(TypeAnnotation(t)); } case TypeOperator::NONE: { return ExpandedType(TypeAnnotation(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::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::NONE; __valueD = number.get(); } Expression::Expression(const Atom& a) : Expression() { __state = STRING; op = Operator::NONE; __valueS = a.get(); } Expression::Expression(const Atom &ident) : Expression() { __state = IDENT; op = Operator::NONE; __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::NONE: __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); } Expression::Expression() : __state(INVALID), op(Operator::NONE), id(nextVacantId++) { } -namespace details { namespace incomplete { +namespace details { namespace inconsistent { AST::AST() { 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::getFunctionVariants(const std::string& name) const { auto functions = __indexFunctions.equal_range(name); 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) { __entry->addBinding(move(name), move(argument)); } const std::string& Function::getName() const { return __name; } ScopedSymbol CodeScope::registerIdentifier(const Expression& identifier) { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); auto result = __identifiers.emplace(identifier.getValueString(), __vCounter); if (result.second) { ++__vCounter; return { __vCounter - 1, version }; } 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) { argument.__state = Expression::BINDING; __bindings.push_back(var.getValueString()); ScopedSymbol binding = registerIdentifier(var); __declarations[binding] = move(argument); } void -CodeScope::addDeclaration(Expression&& var, Expression&& body) { +CodeScope::addDefinition(Expression&& var, Expression&& body) { ScopedSymbol s = registerIdentifier(var); __declarations[s] = move(body); } 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; } Expression& CodeScope::getBody() { return __declarations[ScopedSymbol::RetSymbol]; } const Expression& -CodeScope::getDeclaration(const Symbol& symbol) { +CodeScope::getDefinition(const Symbol& symbol) { CodeScope* self = symbol.scope; - return self->getDeclaration(symbol.identifier); + return self->getDefinition(symbol.identifier); } const Expression& -CodeScope::getDeclaration(const ScopedSymbol& symbol) { +CodeScope::getDefinition(const ScopedSymbol& symbol) { assert(__declarations.count(symbol) && "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(ClaspLayer& 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 5833e4d..5fcb384 100644 --- a/cpp/src/ast.h +++ b/cpp/src/ast.h @@ -1,585 +1,711 @@ /* 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 */ #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 hold for all atoms/identifiers Parser::Token data, like line:col position +//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, ARRAY, STRUCT, ACCESS, LINK }; 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 + * + * This class represents type in denormalized form, i.e. without 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 { ADD, SUB, MUL, DIV, EQU, NE, NEG, LSS, LSE, GTR, GTE, LIST, LIST_RANGE, LIST_NAMED, CALL, CALL_INTRINSIC, NONE, IMPL/* implication */, MAP, FOLD, FOLD_INF, LOOP_CONTEXT, INDEX, IF, SWITCH, SWITCH_ADHOC, SWITCH_VARIANT, CASE, CASE_DEFAULT, LOGIC_AND, ADHOC, CONTEXT_RULE, VARIANT }; class Function; class AST; class CodeScope; class MetaRuleAbstract; -template -struct ManagedPtr { - - static ManagedPtr Invalid() { - return ManagedPtr(); - } - - ManagedPtr() : __storage(0) { - } - - ManagedPtr(unsigned int id, const std::vector* storage) - : __id(id), __storage(storage) { - } - - Target& - operator*() const { - assert(isValid() && "Invalid Ptr"); - return *__storage->at(__id); - } - - void operator=(const ManagedPtr& other) { - __id = other.__id; - __storage = other.__storage; - } - - bool - operator==(const ManagedPtr& other) { - return isValid() && (__id == other.__id); - } - - Target* - operator->() const noexcept { - assert(isValid() && "Invalid Ptr"); - return __storage->at(__id); - } - - inline bool isValid() const { - return (__storage) && (0 <= __id) && (__id < __storage->size()); - } - - inline operator bool() const { - return isValid(); - } - - ManagedPtr& operator++() { - ++__id; - return *this; - } - - inline unsigned int id() const { - return __id; - } - -private: - unsigned int __id = 0; - const std::vector * __storage = 0; -}; - typedef ManagedPtr ManagedFnPtr; typedef ManagedPtr ManagedScpPtr; typedef ManagedPtr ManagedRulePtr; const ManagedScpPtr NO_SCOPE = ManagedScpPtr(UINT_MAX, 0); -//To update ExpressionHints in case of any changes +/** + * \brief Represents every instruction in Xreate's syntax tree + * \attention In case of any changes update 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. + * + * Additionally, `types` as a special kind of annotations use Expression-like data structure TypeAnnotation + * \sa xreate::AST, xreate::TypeAnnotation + */ +// struct Expression { friend class CodeScope; friend class ClaspLayer; 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 ClaspLayer; class LLVMLayer; class MetaRuleAbstract { public: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards); virtual ~MetaRuleAbstract(); virtual void compile(ClaspLayer& layer) = 0; protected: RuleArguments __args; RuleGuards __guards; }; class RuleWarning : public MetaRuleAbstract { friend class ClaspLayer; public: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message); virtual void compile(ClaspLayer& 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; CodeScope * scope; }; template<> struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 7; }; 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 + * + * Holds single expression as a *body* and 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 */ Expression& getBody(); - void addDeclaration(Expression&& var, Expression&& body); + + /** \brief Adds variable definition to be used in body as well as in other declarations */ + void addDefinition(Expression&& var, Expression&& body); + + /** \brief Returns symbols' definition */ + static const Expression& getDefinition(const Symbol& symbol); + const Expression& getDefinition(const ScopedSymbol& symbol); + + /** \brief Adds variable defined elsewhere */ void addBinding(Expression&& var, Expression&& argument); - static const Expression& getDeclaration(const Symbol& symbol); - const Expression& getDeclaration(const ScopedSymbol& symbol); - - ~CodeScope(); - + std::vector __bindings; std::map __identifiers; CodeScope* __parent; //TODO move __definitions to SymbolsAttachments data - //NOTE: definition of return type has zero(0) variable index + //NOTE: definition of return type has index 0 std::unordered_map __declarations; std::vector tags; std::vector contextRules; + private: VNameId __vCounter = 1; ScopedSymbol registerIdentifier(const Expression& identifier); public: bool recognizeIdentifier(const Expression& identifier) const; ScopedSymbol getSymbol(const std::string& alias); }; +/** + * \brief Represents single function in Xreate's syntax tree + * + * Holds an entry code scope and `guardContext` required for function to operate + * \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); + + /** + * \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 guardContext; private: std::map __tags; }; class ExternData; struct ExternEntry { std::string package; std::vector headers; }; typedef Expanded ExpandedType; 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); } }; -namespace details { namespace incomplete { +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&&) + */ class AST { -public: + friend class xreate::TypesResolver; +public: AST(); - - //TASK extern and DFA interfaces move into addInterfaceData + /** - * DFA Interface + * \brief Adds new function to AST + * \param f Function to register */ - void addDFAData(Expression&& data); + void add(Function* f); /** - * Extern Interface + * \brief Adds new declarative rule to AST + * \param r Declarative Rule */ - void addExternData(ExternData&& data); - - void addInterfaceData(const ASTInterface& interface, Expression&& data); - void add(Function* f); - 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); - typedef std::multimap FUNCTIONS_REGISTRY; + /** \brief Returns all function in AST */ std::list getAllFunctions() const; + + /** + * \brief Returns all functions with given name + * \param name Functinos to find + * \return list of found functions + */ std::list getFunctionVariants(const std::string& name) 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 + * \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; - - - // ***** TYPES SECTION ***** -public: + +protected: std::map __indexTypeAliases; - void add(TypeAnnotation t, Atom alias); +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 + * \sa xreate::DFAPass + */ + void addDFAData(Expression&& data); + + /** \brief Stores data for later use by xreate::ExternLayer */ + void addExternData(ExternData&& data); - // ***** SYMBOL RECOGNITION ***** + /** + * \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(); - xreate::AST* finalize(); +///@} }; template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); } } // namespace details::incomplete -class AST : public details::incomplete::AST { +/** + * \brief Xreate's Syntax Tree in 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. + * + * 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). + * \sa xreate::details::inconsistent::AST + */ +class AST : public details::inconsistent::AST { public: - AST() : details::incomplete::AST() {} + 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 9340962..a9f19f3 100644 --- a/cpp/src/attachments.cpp +++ b/cpp/src/attachments.cpp @@ -1,15 +1,20 @@ /* 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/aux/xreatemanager-decorators.cpp b/cpp/src/aux/xreatemanager-decorators.cpp index dd20279..5a8c79e 100644 --- a/cpp/src/aux/xreatemanager-decorators.cpp +++ b/cpp/src/aux/xreatemanager-decorators.cpp @@ -1,70 +1,75 @@ /* 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-decorators.cpp * * Author: pgess * Created on July 16, 2017, 4:40 PM */ +/** + * \file xreatemanager-decorators.h + * \brief \ref xreate::XreateManager decorators to provide various functionality + */ + #include "aux/xreatemanager-decorators.h" #include "main/Parser.h" #include "pass/compilepass.h" #include "pass/adhocpass.h" #include "pass/cfapass.h" #include "pass/dfapass.h" #include "pass/interpretationpass.h" #include "pass/versionspass.h" namespace xreate { void XreateManagerDecoratorBase::prepareCode(std::string&& code){ grammar::main::Scanner scanner(reinterpret_cast(code.c_str()), code.size()); grammar::main::Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Parser errors"); PassManager::prepare(parser.root->finalize()); } void XreateManagerDecoratorBase::prepareCode(FILE* code){ grammar::main::Scanner scanner(code); grammar::main::Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Parser errors"); PassManager::prepare(parser.root->finalize()); } void XreateManagerDecoratorBase::analyse(){ CompilePass::prepareQueries(clasp); clasp->run(); } void XreateManagerDecoratorFull::initPasses(){ cfa::CFAPass* passCFG = new cfa::CFAPass(this); //TODO is it really DFGPass needs CFGpass? registerPass(new dfa::DFAPass(this), PassId::DFGPass, passCFG); registerPass(passCFG, PassId::CFGPass); this->registerPass(new adhoc::AdhocPass(this), PassId::AdhocPass); this->registerPass(new interpretation::InterpretationPass(this), PassId::InterpretationPass); this->registerPass(new versions::VersionsPass(this), PassId::VersionsPass); } void* XreateManagerDecoratorFull::run() { std::unique_ptr compiler(new compilation::CompilePassCustomDecorators<>(this)); compiler->run(); llvm->print(); llvm->initJit(); return llvm->getFunctionPointer(compiler->getEntryFunction()); } } //namespace xreate diff --git a/cpp/src/aux/xreatemanager-modules.h b/cpp/src/aux/xreatemanager-modules.h index ab15bc2..5629527 100644 --- a/cpp/src/aux/xreatemanager-modules.h +++ b/cpp/src/aux/xreatemanager-modules.h @@ -1,113 +1,126 @@ /* 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/). + */ + #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 + * + * Scanning of source file looking for other modules requirements. + * Finds and connects other modules to satisfy module's requirements + * + * \sa ModulesSolver, ModulesRegistry, ModuleRecord + */ + class XreateManagerDecoratorModules: public Parent{ public: XreateManagerDecoratorModules(): __registry(new ModulesRegistry()){} 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: ModulesRegistry* __registry; void parseModulesGrammar(Scanner& scanner, std::list& listIncludedFiles){ ModulesSolver solver(__registry); 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(__registry); 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::incomplete::AST* ast = new AST; + 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/clasplayer.cpp b/cpp/src/clasplayer.cpp index e1b6ec5..8294af4 100644 --- a/cpp/src/clasplayer.cpp +++ b/cpp/src/clasplayer.cpp @@ -1,343 +1,387 @@ /* 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: clasplayer.cpp */ +/** + * \file clasplayer.h + * \brief Resoner. Wrapper over Clasp reasoner library + */ + #include "clasplayer.h" #include #include "utils.h" #include #include #include #include "analysis/aux.h" #include "analysis/DominatorsTreeAnalysisProvider.h" #include "analysis/cfagraph.h" #include "analysis/dfagraph.h" using namespace std; //TODO escape identifiers started with upper case symbol namespace xreate { void ClaspLayer::printWarnings(std::ostream& out) { const std::string warningTag = "warning"; auto warningsRange = __model.equal_range(warningTag); for (auto warning=warningsRange.first; warning!= warningsRange.second; ++warning) { unsigned int warningId; Gringo::Symbol params; std::tie(warningId, params) = parse(warning->second); cout << "Warning: " << __warnings.at(warningId) << " "; params.print(out); out< warnings; cout << "Model: " << endl; const string& atomBindVar = Config::get("clasp.bindings.variable"); const string& atomBindFunc = Config::get("clasp.bindings.function"); const string& atomBindScope = Config::get("clasp.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 name = std::get<1>(parse(atom)).name().c_str(); __model.emplace(move(name), move(atom)); } __model.emplace(atomName, move(atom)); } return true; } void ClaspLayer::setCFAData(xreate::cfa::CFAGraph* graph) { dataCFA.reset(graph); } void ClaspLayer::setDFAData(xreate::dfa::DFAGraph* graph){ dataDFA.reset(graph); } void ClaspLayer::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, ", ")) <__rawImports) { std::ifstream file(fn); if (!file) continue; while(!file.eof()){ string line; std::getline(file, line); out << line << endl; } } } void ClaspLayer::addRawScript(std::string&& script){ __partGeneral << script; } void ClaspLayer::run() { involveImports(); if (this->dataDFA){ this->dataDFA->print(__partGeneral); } if (this->dataCFA){ this->dataCFA->print(__partGeneral); } dominators::DominatorsTreeAnalysisProvider providerDominators; providerDominators.run(this); providerDominators.print(__partGeneral); 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->handleSolution(model); return true; }, {}); if (result.satisfiable() == Gringo::SolveResult::Satisfiable) { cout << FGRN("SUCCESSFULLY") << endl; } else { cout << FRED("UNSUCCESSFULLY") << endl; } // invoke all query plugins to process clasp data for (auto q: __queries) { q.second->init(this); } } ClaspLayer::ClaspLayer() { } ClaspLayer::ModelFragment ClaspLayer::query(const std::string& atom) { if (! __model.count(atom)){ return boost::none; } return ModelFragment(__model.equal_range(atom)); } ScopePacked ClaspLayer::pack(CodeScope* const scope) { auto pos = __indexScopes.emplace(scope, __indexScopes.size()); if (pos.second) __registryScopes.push_back(scope); return pos.first->second; } size_t ClaspLayer::getScopesCount() const{ return __registryScopes.size(); } SymbolPacked ClaspLayer::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 ClaspLayer::unpack(const SymbolPacked& symbol) { return Symbol{ScopedSymbol{symbol.identifier, symbol.version}, __registryScopes[symbol.scope]}; }; std::string ClaspLayer::getHintForPackedSymbol(const SymbolPacked& symbol){ if (!symbol.categoryTransient) { auto result = __indexSymbolNameHints.find(symbol); return (result == __indexSymbolNameHints.end())? "" : result->second; } else { return "anonym(" + to_string(symbol.identifier) + ")"; } } 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); } IQuery* ClaspLayer::registerQuery(IQuery *query, const QueryId& id) { return __queries.emplace(id, query).first->second; } IQuery* ClaspLayer::getQuery(const QueryId& id){ assert(__queries.count(id) && "Undefined query"); return __queries.at(id); } 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); } } } 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 = ClaspLayer::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; } std::list ParseImplAtom>::get(const Gringo::Symbol& atom){ assert (atom.type() == Gringo::SymbolType::Fun); std::list result; for (const Gringo::Symbol& arg: atom.args()) { result.push_back(ParseImplAtom::get(arg)); } return result; } } //end of xreate namespace + +/** + * \class xreate::ClaspLayer + * \brief Reasoning and logic Solver. + * + * Wraps external brilliant fantastic tool [Clasp solver](https://potassco.org/clasp/) + * + * For building *logic program* for reasoning ClaspLayer 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::DominatorsTreeAnalysisProvider. + * Executed by \ref run() + * - Context rules. See xreate::ContextRule and general [Context Explanation](/w/concepts/context) + * + * 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 + * + * Once ClaspLayer got input from all providers and logic program is fully constructed + * it runs external Clasp solver and receives back desired solutions. + * + * Output of 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::DominatorsTreeAnalysisProvider + */ \ No newline at end of file diff --git a/cpp/src/clasplayer.h b/cpp/src/clasplayer.h index d4ae821..03ef0e8 100644 --- a/cpp/src/clasplayer.h +++ b/cpp/src/clasplayer.h @@ -1,204 +1,228 @@ /* 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: clasplayer.h */ #ifndef CLASPLAYER_H #define CLASPLAYER_H #include "ast.h" #include "contextrule.h" #include #include #include #include #include #include #include #include namespace xreate { typedef unsigned int ScopePacked; struct SymbolPacked { VNameId identifier; versions::VariableVersion version; ScopePacked scope; bool categoryTransient; SymbolPacked(): categoryTransient(false){} SymbolPacked(ScopedSymbol i, ScopePacked s, bool isTransient = false): identifier(i.id), version(i.version), scope(s), categoryTransient(isTransient){} SymbolPacked(VNameId symbolId, versions::VariableVersion symbolVersion, ScopePacked symbolScope, bool isTransient = false) : identifier(symbolId), version(symbolVersion), scope(symbolScope), categoryTransient(isTransient){} }; bool operator==(const SymbolPacked& s1, const SymbolPacked& s2); bool operator<(const SymbolPacked& s1, const SymbolPacked& s2); enum class DFGConnection { STRONG, WEAK, PROTOTYPE }; -class IAnalysisData { +/** \brief Designated to mark analysis results that can be composed as *logic program* */ +class IAnalysisReport { public: + /** \brief Composes *logic program* based on analysis data into ASP format and appends to a stream*/ void print(std::ostringstream& output) const; - virtual ~IAnalysisData(){}; + virtual ~IAnalysisReport(){}; }; +/** \brief Logic program query interface */ class IQuery { public: virtual void init(ClaspLayer* clasp) = 0; virtual ~IQuery() {} }; enum class QueryId { ContainersQuery, ContextQuery, PtrvalidQuery }; namespace dfa{ class DFAGraph; } namespace cfa { class CFAGraph; } class ClaspLayer { friend class ContextRule; - //PROVIDERS: -public: - boost::scoped_ptr dataDFA; + /**\name Data Providers Management */ +///@{ +public: + /** \brief Provides DFA graph as output from xreate::DFAPass */ void setDFAData(xreate::dfa::DFAGraph* graph); - boost::scoped_ptr dataCFA; + /** \brief Provides CFA graph as output from xreate::CFAPass */ void setCFAData(xreate::cfa::CFAGraph* graph); + /** \brief Appends arbitrary string to *logic program + */ void addRawScript(std::string&& script); + boost::scoped_ptr dataDFA; + boost::scoped_ptr dataCFA; + private: + /** Includes external text files to a *logic program* */ void involveImports(); +///@} - //QUERIES + /**\name Queries Management */ +///@{ public: + /** \brief Adds query. See xreate::IQuery */ IQuery* registerQuery(IQuery* query, const QueryId& id); + + /** \brief Returns particular query. See xreate::IQuery */ IQuery* getQuery(const QueryId& id); template static std::tuple parse(const Gringo::Symbol& atom); typedef std::multimap::const_iterator ModelIterator; typedef boost::optional> ModelFragment; ModelFragment query(const std::string& atom); size_t getScopesCount() const; SymbolPacked pack(const Symbol& symbol, std::string hintSymbolName = ""); ScopePacked pack(CodeScope * const scope); Symbol unpack(const SymbolPacked& symbol); std::string getHintForPackedSymbol(const SymbolPacked& symbol); - +///@} + private: std::map __queries; std::multimap __model; std::map __indexSymbolNameHints; std::unordered_map __indexScopes; std::vector __registryScopes; - //WARNINGS - //TODO move to separate provider/query + /**\name Diagnostic */ +///@{ +//TODO diagnostic move over to separate provider/query public: + /** \brief Adds diagnostic rule */ void addRuleWarning(const RuleWarning &rule); + + /** \brief Registers diagnostic messages */ unsigned int registerWarning(std::string &&message); private: std::map __warnings; void printWarnings(std::ostream& out); - - //DEFAULT +///@} + +///@{ public: - AST *ast; - ClaspLayer(); + + /** \brief Executes reasoning */ void run(); - +///@} + AST *ast; + private: + std::ostringstream __partTags; std::ostringstream __partGeneral; bool handleSolution(Gringo::Model const &model); }; template struct ParseImplAtom { static typ get(const Gringo::Symbol& atom) { return atom.num(); } }; 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 Gringo::Symbol get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom>{ static std::list get(const Gringo::Symbol& atom); }; 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 ClaspLayer::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/compilation/advanced.cpp b/cpp/src/compilation/advanced.cpp index 673d7e8..8e08600 100644 --- a/cpp/src/compilation/advanced.cpp +++ b/cpp/src/compilation/advanced.cpp @@ -1,403 +1,407 @@ /* 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 */ -//#include +/** + * \file advanced.h + * \brief Compilation of statements that require more than one LLVM instruction + */ + #include "compilation/advanced.h" #include "compilation/containers.h" #include "compilation/transformersaturation.h" #include "query/context.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; Advanced::Advanced(compilation::Context ctx) : context(ctx), tyNum(static_cast (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Num))))) { } llvm::Value* Advanced::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::getGlobalContext(), "loop", function->raw); llvm::BasicBlock *blockBeforeLoop = builder.GetInsertBlock(); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "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* Advanced::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* Advanced::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) { //TODO DISABLEDFEATURE validptr // TODO review safety check: validPtr for `aggregate` // SECTIONTAG validptr exception // std::vector refs; // llvm::BasicBlock *blockSafe = llvm::BasicBlock::Create(llvm::getGlobalContext(), "safe", function->raw); // PointerType* tyAggr = dyn_cast(aggregate->getType()); // llvm::Value* null = llvm::ConstantPointerNull::get(tyAggr); // Value* condNull = llvm->builder.CreateICmpNE(aggregate, null); // // llvm::BasicBlock *blockException = llvm::BasicBlock::Create(llvm::getGlobalContext(), "exception", function->raw); // llvm->builder.CreateCondBr(condNull, blockSafe, blockException); // llvm->initExceptionBlock(blockException); // // llvm->builder.SetInsertPoint(blockSafe); //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* Advanced::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::getGlobalContext(), "fold", function->raw); llvm::BasicBlock *blockLoopBody = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold_body", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold_after", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm::getGlobalContext(), "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* Advanced::compileFoldInf(const Expression& fold, const std::string& hintRetVar) { EXPAND_CONTEXT assert(fold.op == Operator::FOLD_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::getGlobalContext(), "foldinf", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm::getGlobalContext(), "foldinf_next", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "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* Advanced::compileIf(const Expression& exprIf, const std::string& hintRetVar) { EXPAND_CONTEXT //initialization: const Expression& condExpr = exprIf.getOperands()[0]; llvm::IRBuilder<>& builder = llvm->builder; //llvm::Type* tyResultType = llvm->toLLVMType(llvm->ast->expandType(exprIf.type)); llvm::BasicBlock *blockAfter = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifAfter", function->raw); llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifTrue", function->raw); llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifFalse", function->raw); llvm::Value* cond = scope->process(condExpr); llvm->builder.CreateCondBr(cond, blockTrue, blockFalse); builder.SetInsertPoint(blockTrue); CodeScope* scopeTrue = exprIf.blocks.front(); llvm::Value* resultTrue = function->getScopeUnit(scopeTrue)->compile(); blockTrue = builder.GetInsertBlock(); builder.CreateBr(blockAfter); builder.SetInsertPoint(blockFalse); CodeScope* scopeFalse = exprIf.blocks.back(); llvm::Value* resultFalse = function->getScopeUnit(scopeFalse)->compile(); blockFalse = builder.GetInsertBlock(); builder.CreateBr(blockAfter); builder.SetInsertPoint(blockAfter); llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if")); ret->addIncoming(resultTrue, blockTrue); ret->addIncoming(resultFalse, blockFalse); return ret; } //TODO Switch: default variant no needed when all possible conditions are considered llvm::Value* Advanced::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) { EXPAND_CONTEXT AST* root = context.pass->man->root; UNUSED(function); 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::IRBuilder<>& builder = llvm->builder; llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm::getGlobalContext(), "switchAfter", function->raw); builder.SetInsertPoint(blockEpilog); llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch)); llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch")); builder.SetInsertPoint(blockProlog); llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]); llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm::getGlobalContext(), "caseDefault", function->raw); llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(conditionSwitch, blockDefault, countCases); for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm::getGlobalContext(), "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(condCase), 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; } //TODO recognize cases to make const arrays/stored in global mem/stack alloced. llvm::Value* Advanced::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::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::getGlobalContext()), 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* Advanced::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(function); UNUSED(scope); Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm::getGlobalContext())); //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::getGlobalContext(), data); Value* rawPtrData = llvm->builder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm::getGlobalContext()), 1, false)); llvm->builder.CreateStore(rawData, rawPtrData); return llvm->builder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar); } diff --git a/cpp/src/compilation/containers.cpp b/cpp/src/compilation/containers.cpp index c4c8049..c50d43a 100644 --- a/cpp/src/compilation/containers.cpp +++ b/cpp/src/compilation/containers.cpp @@ -1,203 +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/. - * + * * File: containers.cpp * Author: pgess + * + * \file compilation/containers.h + * \brief Containers compilation support. See more [details on Containers](/w/concepts/containers) */ #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::getGlobalContext()); return llvm::ConstantInt::get(Type::getInt32Ty(llvm::getGlobalContext()), 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::getGlobalContext()), 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::getDeclaration(current); + 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::Advanced(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::NONE: { //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::getGlobalContext()), 1), hintRetVar); default: break; } if (linkedlist){ - ExpandedType tySource = llvm->ast->getType(CodeScope::getDeclaration(source)); + ExpandedType tySource = llvm->ast->getType(CodeScope::getDefinition(source)); assert(tySource->__operator == TypeOperator::ARRAY && "Linked list implementation has to have ARRAY type"); assert(tySource->__operands.size()); return xreate::compilation::Advanced(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::getGlobalContext()), 0); } llvm::Value* IteratorForward::end(){ //length return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), __length); } llvm::Value* IteratorForward::get(llvm::Value* index,const std::string& hintRetVar){ //GEP[index]] llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm::getGlobalContext()); 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::getGlobalContext()); 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 148deb5..5a190f3 100644 --- a/cpp/src/compilation/containers.h +++ b/cpp/src/compilation/containers.h @@ -1,88 +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/. * * File: containers.h * Author: pgess */ #ifndef CODEINSTRUCTIONS_H #define CODEINSTRUCTIONS_H #include "ast.h" #include "llvmlayer.h" #include "pass/compilepass.h" #include "compilation/advanced.h" #include "query/context.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 + * \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 + * \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; 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::getDeclaration(source)), + 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 + * \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/latecontextcompiler2.cpp b/cpp/src/compilation/latecontextcompiler2.cpp index 1468036..43f9621 100644 --- a/cpp/src/compilation/latecontextcompiler2.cpp +++ b/cpp/src/compilation/latecontextcompiler2.cpp @@ -1,198 +1,201 @@ /* 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/. - * + * * LateContextCompiler2.cpp * * Created on: 02/10, 2016 * Author: pgess + * + * \file latecontextcompiler2.h + * \brief Late context compilation. See more on [Late Context](/w/concepts/context#late-context) */ //TOTEST default variants - do not enable specialization context in order to check default variant invocation #include "latecontextcompiler2.h" #include "llvmlayer.h" #include "pass/compilepass.h" #include "query/context.h" #include using namespace std; namespace xreate {namespace context{ const string topicSpecializationAtom = "specialization"; const string topicDependencyAtom = "dependency"; typedef ExpressionSerialization::Code DomainId; typedef ExpressionSerialization::Serializer Domain; //TODO implement variantDefault; llvm::Value* compileDecisionSelector(llvm::IRBuilder<>& builder, llvm::Value* selector, std::vector vectorVariants, llvm::Value* variantDefault=nullptr){ assert(vectorVariants.size()>0); llvm::IntegerType* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::Type* tyElement = vectorVariants[0]->getType(); llvm::Type* tyVariants = llvm::VectorType::get(tyElement, vectorVariants.size()); llvm::Value* vectorRaw = llvm::ConstantVector::getNullValue(tyVariants); for(DomainId i=0; iqueryContext; __sizeOfDemand = context->getFunctionDemand(function->function->getName()).size(); } llvm::Value* LateContextCompiler2::findFunction(const std::string& calleeName, llvm::Function* specializationDefault, ScopePacked scopeCaller){ const string& functionName = function->function->getName(); ContextQuery* context = pass->queryContext; llvm::IRBuilder<>& builder = pass->man->llvm->builder; const FunctionDemand& demand = context->getFunctionDemand(functionName); const std::list& specializations = pass->man->root->getFunctionVariants(calleeName); //independent decision: Expression topic(Operator::CALL, {(Atom(string(topicSpecializationAtom))), Expression(Operator::CALL, {Atom(string(calleeName))}), Atom(scopeCaller)}); assert(demand.right.count(topic) && "Can't determine specialization for the function"); size_t topicId = demand.right.at(topic); llvm::Value* topicDecisionRaw = builder.CreateExtractValue(this->rawContextArgument, llvm::ArrayRef{(unsigned) topicId}); const Domain& specializationsDomain= context->getTopicDomain(topic); std::vector vectorVariants; vectorVariants.reserve(specializationsDomain.size()); for (const ManagedFnPtr& f: specializations){ if (!f->guardContext.isValid()) continue; const auto& variantId = specializationsDomain.getIdOptional(f->guardContext); if (variantId){ if (vectorVariants.size() < *variantId + 1) { vectorVariants.resize(*variantId + 1); } vectorVariants[*variantId] = pass->getFunctionUnit(f)->compile(); } } return compileDecisionSelectorAsSwitch(topicDecisionRaw, vectorVariants, specializationDefault); } llvm::Value* LateContextCompiler2::compileContextArgument(const std::string& callee, ScopePacked scopeCaller){ const std::string& atomDependentDecision = Config::get("clasp.context.decisions.dependent"); llvm::IRBuilder<>& builder = pass->man->llvm->builder; ContextQuery* context = pass->queryContext; const string& functionName = function->function->getName(); const Decisions& dictStaticDecisions = context->getFinalDecisions(scopeCaller); const FunctionDemand& demandCallee = context->getFunctionDemand(callee); const FunctionDemand& demandSelf = context->getFunctionDemand(functionName); llvm::IntegerType* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::Type* tyDemand = llvm::ArrayType::get(ty32, demandCallee.size()); //builder.CreateAlloca(tyDemand, llvm::ConstantInt::get(ty32, 1)); llvm::Value* res = llvm::ConstantArray::getNullValue(tyDemand); for (size_t i=0, size = demandCallee.size(); irawContextArgument, llvm::ArrayRef{(unsigned) topicId}); } else if (dictStaticDecisions.count(topic)){ //static final decision: const Expression& decision = dictStaticDecisions.at(topic); const Domain& domainOfTopic = context->getTopicDomain(topic); const DomainId& decisionCode = domainOfTopic.getId(decision); decisionRaw = llvm::ConstantInt::get(ty32, decisionCode); } else { //dependent decision decisionRaw = compileDependentDecision(topic, scopeCaller); } res = builder.CreateInsertValue(res, decisionRaw, llvm::ArrayRef{(unsigned) i}); } return res; } llvm::Value* LateContextCompiler2::compileDependentDecision(const Expression& topic, ScopePacked scopeCaller){ const string& functionName = function->function->getName(); ContextQuery* context = pass->queryContext; llvm::IRBuilder<>& builder = pass->man->llvm->builder; llvm::IntegerType* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext()); const FunctionDemand& demandSelf = context->getFunctionDemand(functionName); const Expression topicDependency = Expression(Operator::CALL, {Atom(string(topicDependencyAtom)), topic, Atom(scopeCaller)}); const Domain& demandOfTopic = context->getTopicDomain(topic); const Domain& domainOfTopicDependency = context->getTopicDomain(topicDependency); //dependent decision vector vectorDecisions(domainOfTopicDependency.size(), llvm::UndefValue::get(ty32)); for (const std::pair& entry: context->getDependentDecision(scopeCaller,topic)){ vectorDecisions[domainOfTopicDependency.getId(entry.first)]= llvm::ConstantInt::get(ty32, demandOfTopic.getId(entry.second)); } size_t topicDependencyId = demandSelf.right.at(topicDependency); llvm::Value* decisionRaw = builder.CreateExtractValue(this->rawContextArgument, llvm::ArrayRef{(unsigned) topicDependencyId}); auto result = compileDecisionSelector(pass->man->llvm->builder, decisionRaw, vectorDecisions); return result; } llvm::Value* LateContextCompiler2::compileDecisionSelectorAsSwitch(llvm::Value* selector, std::vector vectorVariants, llvm::Function* variantDefault){ llvm::IRBuilder<>& builder = pass->man->llvm->builder; llvm::IntegerType* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::BasicBlock* blockDefault = llvm::BasicBlock::Create(llvm::getGlobalContext(), "caseDefault", this->function->raw); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm::getGlobalContext(), "VariantDeterminationEnd", this->function->raw); llvm::SwitchInst* instrSwitch = builder.CreateSwitch(selector, blockDefault, vectorVariants.size()); builder.SetInsertPoint(blockEpilog); llvm::PHINode *result = builder.CreatePHI(variantDefault->getType(), vectorVariants.size(), "callee"); for (size_t i=0; ifunction->raw); builder.SetInsertPoint(blockCase); builder.CreateBr(blockEpilog); result->addIncoming(vectorVariants[i], blockCase); instrSwitch->addCase(llvm::ConstantInt::get(ty32, i), blockCase); } builder.SetInsertPoint(blockDefault); builder.CreateBr(blockEpilog); result->addIncoming(variantDefault, blockDefault); builder.SetInsertPoint(blockEpilog); return result; } size_t LateContextCompiler2::getFunctionDemandSize() const { return __sizeOfDemand; } }} /* namespace xreate::context */ diff --git a/cpp/src/compilation/latecontextcompiler2.h b/cpp/src/compilation/latecontextcompiler2.h index b895641..faa33b2 100644 --- a/cpp/src/compilation/latecontextcompiler2.h +++ b/cpp/src/compilation/latecontextcompiler2.h @@ -1,54 +1,60 @@ /* 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/. * * LateContextCompiler2.h * * Created on: 02/10, 2016 * Author: pgess */ //TOTEST compile several context arguments #ifndef LATECONTEXTCOMPILER2_H_ #define LATECONTEXTCOMPILER2_H_ #include "serialization.h" namespace llvm { class Value; class Function; } namespace xreate { class CompilePass; namespace compilation { class IFunctionUnit; }} namespace xreate {namespace context{ typedef unsigned int ScopePacked; + +/** \brief Encapsulates compilation of Late Context + * \sa xreate::context::ContextQuery, xreate::compilation::BasicCodeScopeUnit + */ class LateContextCompiler2 { public: llvm::Value* rawContextArgument = nullptr; LateContextCompiler2(compilation::IFunctionUnit* f, CompilePass* p); + + /** \brief Finds function with regard to late context*/ llvm::Value* findFunction(const std::string& calleeName, llvm::Function* specializationDefault, ScopePacked scopeCaller); llvm::Value* compileContextArgument(const std::string& callee, ScopePacked scopeCaller); size_t getFunctionDemandSize() const; private: //boost::bimap __decisions; //std::vector __scheme; compilation::IFunctionUnit* function; CompilePass* pass; size_t __sizeOfDemand; llvm::Value* compileDependentDecision(const Expression& topic, ScopePacked scopeCaller); llvm::Value* compileDecisionSelectorAsSwitch(llvm::Value* selector, std::vector vectorVariants, llvm::Function* variantDefault); }; }} /* namespace xreate::context */ #endif /* LATECONTEXTCOMPILER2_H_ */ diff --git a/cpp/src/compilation/operators.cpp b/cpp/src/compilation/operators.cpp index bef5747..2c93079 100644 --- a/cpp/src/compilation/operators.cpp +++ b/cpp/src/compilation/operators.cpp @@ -1,70 +1,75 @@ /* 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_NAMED)){ 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/scopedecorators.h b/cpp/src/compilation/scopedecorators.h index 5671bcb..5e2affe 100644 --- a/cpp/src/compilation/scopedecorators.h +++ b/cpp/src/compilation/scopedecorators.h @@ -1,132 +1,142 @@ /* 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" namespace xreate { class CompilePass; namespace compilation { class ICodeScopeUnit; class IFunctionUnit; +/**\brief Caching ability for code scope compilation + * \extends xreate::compilation::ICodeScopeUnit + */ template class CachedScopeDecorator: public Parent{ typedef CachedScopeDecorator SELF; public: CachedScopeDecorator(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} void 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; } 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{ 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::getDeclaration(s); + Expression declaration = CodeScope::getDefinition(s); if (!declaration.isDefined()){ if (self->__declarationsOverriden.count(s.identifier)){ declaration = self->__declarationsOverriden[s.identifier]; } else { assert(false); //in case of binding there should be raws provided. } } return self->__rawVars[s.identifier] = Parent::processSymbol(s, hintRetVar); } void overrideDeclaration(const Symbol binding, Expression&& declaration){ SELF* self = dynamic_cast(Parent::function->getScopeUnit(binding.scope)); self->__declarationsOverriden.emplace(binding.identifier, std::move(declaration)); } 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 Default code scope compilation functionality*/ typedef CachedScopeDecorator< compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< versions::VersionsScopeDecorator>>> DefaultCodeScopeUnit; } //end of compilation namespace struct CachedScopeDecoratorTag; struct VersionsScopeDecoratorTag; template<> struct DecoratorsDict{ typedef compilation::CachedScopeDecorator< 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 569db6c..3c645f1 100644 --- a/cpp/src/compilation/targetinterpretation.cpp +++ b/cpp/src/compilation/targetinterpretation.cpp @@ -1,569 +1,594 @@ /* 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/) + */ + #include "compilation/targetinterpretation.h" #include "pass/interpretationpass.h" #include "analysis/typeinference.h" #include "llvmlayer.h" #include "compilation/scopedecorators.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)); Expression representAsAnnotation(const Gringo::Symbol& atom){ switch (atom.type()) { case Gringo::SymbolType::Num: { Expression result(Operator::VARIANT, {Expression(atom.num())}); result.setValueDouble(0); return result; } case Gringo::SymbolType::Str: { Expression result(Operator::VARIANT, {Expression(Atom(std::string(atom.string().c_str())))}); result.setValueDouble(1); return result; } case Gringo::SymbolType::Fun: { Expression fnDescription(Operator::LIST_NAMED, {}); std::list> bindings{Atom("name"), Atom("arguments")}; fnDescription.addBindings(bindings.begin(), bindings.end()); fnDescription.addArg(Expression(Atom(std::string(atom.name().c_str())))); Expression args(Operator::LIST, {}); for (const Gringo::Symbol& arg : atom.args()) { args.addArg(representAsAnnotation(arg)); } fnDescription.addArg(std::move(args)); Expression result(Operator::VARIANT, {fnDescription}); result.setValueDouble(2); return result; } default: { assert(false); } } } 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; igetScope(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->overrideBinding(value, identCondition); } return *blockFound; } llvm::Value* InterpretationScope::compileHybrid(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->overrideDeclaration(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 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; ireset(); unitBody->reset(); Expression exprElement = elementsInput[i]; intrBody->overrideBinding(exprElement, nameEl); unitBody->overrideDeclaration(symbEl, move(exprElement)); unitBody->bindArg(rawAccum, string(idAccum)); rawAccum = unitBody->compile(); } return rawAccum; } /* case FOLD_INF_INTERPRET_INOUT{ } */ 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 CallStatementRaw(raw, man->pass->man->llvm)); return (*statement)(move(argsActual)); } default: break; } assert(false&& "Unknown hybrid 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 compileHybrid(data.op, expression, context); } Expression result = process(expression); return context.scope->process(result); } Expression InterpretationScope::process(const Expression& expression){ 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(); iprocess(args); } case Operator::CALL_INTRINSIC: { std::string nameFunction = expression.getValueString(); if(nameFunction=="query"){ assert(expression.operands.size() == 1); assert(expression.operands.front().__state == Expression::STRING); std::string namePredicate = expression.operands.front().getValueString(); ClaspLayer::ModelFragment model = (static_cast(function->man))->pass->man->clasp->query(namePredicate); Expression result(Operator::LIST, {}); if(model) for (const auto& row: boost::make_iterator_range(model.get())) { result.addArg(representAsAnnotation(std::get<1>(row))); } return result; } else { assert(false && "Unknown intrinsic"); } } 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::INDEX: { Expression exprData = process(expression.operands[0]); for (size_t keyId=1; keyIdgetScope(expression.blocks.front()); Expression accum = exprInit; for(size_t size=exprInput.getOperands().size(), i=0; ioverrideBinding(exprInput.getOperands()[i], argEl); body->overrideBinding(accum, argAccum); accum = body->processScope(); } return accum; } // 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); for(size_t i=0, size = args.size(); ioverrideBinding(args.at(i), string(body->scope->__bindings.at(i))); } 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 prepareArguments(){ 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(){ 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(){ 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); for(size_t no=0, sigNo=0, size = entry->__bindings.size(); no < size; ++no){ if (functionData.signature.at(no) == INTR_ONLY){ entryIntrp->overrideBinding(signatureInstance.bindings[sigNo], entry->__bindings[no]); VNameId argId = entry->__identifiers.at(entry->__bindings[no]); Symbol argSymbol{ScopedSymbol{argId, versions::VERSION_NONE}, entry}; entryUnit->overrideDeclaration(argSymbol, Expression(signatureInstance.bindings[sigNo])); ++sigNo; } } } 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 + *\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) + * + * Holds list of InterpretationFunction / PIFunction to represent interpretation process for 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) + * + */ \ No newline at end of file diff --git a/cpp/src/compilation/targetinterpretation.h b/cpp/src/compilation/targetinterpretation.h index d77f733..99a29d7 100644 --- a/cpp/src/compilation/targetinterpretation.h +++ b/cpp/src/compilation/targetinterpretation.h @@ -1,132 +1,139 @@ /* 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 "clasplayer.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 */ class InterpretationScope: public compilation::Scope{ typedef Scope Parent; public: InterpretationScope(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* compileHybrid(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 */ class InterpretationFunction: public compilation::Function{ public: InterpretationFunction(const ManagedFnPtr& function, compilation::Target* target); Expression process(const std::vector& args); }; -/* - * Partially interpreted function signature - */ +/** \brief Signature of a partially interpreted function */ struct PIFSignature{ ManagedFnPtr declaration; std::vector bindings; }; class PIFunctionUnit; +/** \brief 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 */ 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); private: InterpretationScope* transformContext(const compilation::Context& c); }; +/**\brief Interpretation-aware Code Scope decorator + * \extends xreate::compilation::ICodeScopeUnit + */ template class InterpretationScopeDecorator: public Parent{ public: InterpretationScopeDecorator(CodeScope* 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); } }; +/** \brief translates Logic expression(Gringo::Symbol) into Xreate expression to support intrinsic function `query` */ Expression representAsAnnotation(const Gringo::Symbol& symbol); }} //end of xreate:: interpretation #endif /* TARGETSTATIC_H */ //transformers: // template<> // struct TransformerInfo { // static const int id = 1; // }; diff --git a/cpp/src/compilation/targets.h b/cpp/src/compilation/targets.h index 19bfa24..2da1ce4 100644 --- a/cpp/src/compilation/targets.h +++ b/cpp/src/compilation/targets.h @@ -1,194 +1,201 @@ /* 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: targetabstract.h * Author: pgess * * Created on July 2, 2016, 1:25 PM */ +/** + * \file + * \brief Compilation targets infrastructure + */ + + #ifndef TARGETABSTRACT_H #define TARGETABSTRACT_H #include "ast.h" #include #include namespace xreate{ namespace compilation { template struct TargetInfo{ //typedef Result //typedef Function //typedef Scope }; template class Function; template class Target; template class Scope{ typedef typename TargetInfo::Scope Self; public: CodeScope* scope; typename TargetInfo::Result processSymbol(const Symbol& s){ CodeScope* scope = s.scope; Self* self = function->getScope(scope); if (self->__bindings.count(s.identifier)) { return self->__bindings[s.identifier]; } - const Expression& declaration = CodeScope::getDeclaration(s); + const Expression& declaration = CodeScope::getDefinition(s); if (!declaration.isDefined()){ assert(false); //for bindings there should be result already } return self->__bindings[s.identifier] = self->process(declaration); } typename TargetInfo::Result processScope() { if (raw) return *raw; raw = process(scope->getBody()); return *raw; } // typename TargetInfo::Result // processFunction(typename TargetInfo::Function* fnRemote, const std::vector::Result>& args){ // Scope scopeRemote = fnRemote->getScope(fnRemote->__function->__entry); // // if (scopeRemote->raw){ // return scopeRemote->raw; // } // // return fnRemote->process(args); // } virtual typename TargetInfo::Result process(const Expression& expression)=0; Scope(CodeScope* codeScope, Function* f) : scope(codeScope), function(f) {} virtual ~Scope(){} void overrideBinding(typename TargetInfo::Result arg, const std::string& name){ assert(scope->__identifiers.count(name)); ScopedSymbol id{scope->__identifiers.at(name), versions::VERSION_NONE}; __bindings[id] = arg; //reset the result if any: raw.reset(); } void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __bindings.clear(); __childScopes.clear(); raw.reset(); } protected: Function* function=0; std::map::Result> __bindings; std::list> __childScopes; typename boost::optional::Result> raw; }; template class Function{ typedef typename TargetInfo::Result Result; typedef typename TargetInfo::Scope ConcreteScope; public: Function(const ManagedFnPtr& function, Target* target) : man(target), __function(function) {} virtual ~Function(){}; ConcreteScope* getScope(CodeScope* scope){ if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result){ return result.get(); } } std::shared_ptr unit(new ConcreteScope(scope, this)); if (scope->__parent != nullptr){ getScope(scope->__parent)->registerChildScope(unit); } else { assert(!__entryScope); __entryScope = unit; } if (!__scopes.emplace(scope, unit).second){ __scopes[scope] = unit; } return unit.get(); } virtual Result process(const std::vector& args)=0; Target* man=0; ManagedFnPtr __function; protected: std::map> __scopes; std::shared_ptr __entryScope; }; +/** \brief Similar to xreate::IPass */ template class Target { typedef typename TargetInfo::Function ConcreteFunction; public: Target(AST* root): ast(root){} ConcreteFunction* getFunction(const ManagedFnPtr& function){ unsigned int id = function.id(); if (!__functions.count(id)){ ConcreteFunction* unit = new ConcreteFunction(function, this); __functions.emplace(id, unit); return unit; } return __functions.at(id); } AST* ast; virtual ~Target(){ for (const auto& entry: __functions){ delete entry.second; } } protected: std::map __functions; }; }} #endif /* TARGETABSTRACT_H */ diff --git a/cpp/src/compilation/transformations.cpp b/cpp/src/compilation/transformations.cpp index 4b63fe4..34cc7fd 100644 --- a/cpp/src/compilation/transformations.cpp +++ b/cpp/src/compilation/transformations.cpp @@ -1,32 +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/. - * + * * transformation.cpp * * Author: pgess * Created on March 27, 2017, 4:04 PM */ +/** + * \file transformations.h + * \brief Not used. + */ + + #include "transformations.h" namespace xreate { namespace compilation { std::list TransformationsManager::getRelevantTransformers(const Expression& expression){ std::list result; for (auto tag: expression.tags) { if (__subscriptions.count(tag.first)){ auto handlers = __subscriptions.equal_range(tag.first); for (auto handler = handlers.first; handler != handlers.second; ++handler){ result.push_back(__transformers[handler->second]); } } } return result; } } } //namespace xreate diff --git a/cpp/src/compilation/transformations.h b/cpp/src/compilation/transformations.h index 2fd4473..d9c3a67 100644 --- a/cpp/src/compilation/transformations.h +++ b/cpp/src/compilation/transformations.h @@ -1,113 +1,116 @@ /* 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: transformations.h * Author: pgess * * Created on March 25, 2017, 9:04 PM */ #ifndef TRANSFORMATIONS_H #define TRANSFORMATIONS_H #include "pass/compilepass.h" namespace xreate { namespace compilation { template struct TransformerInfo { //static const unsigned int id = 1; (current vacant id) }; class Transformer{ public: virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx)=0; virtual ~Transformer(){}; }; class TransformationsManager { public: std::list getRelevantTransformers(const Expression& expression); template void registerTransformer(const std::string& annotation, TransformerType* t){ const int id = TransformerInfo::id; assert(!__transformers.count(id)); __transformers[id] = t; __subscriptions.emplace(annotation, id); } template void unregisterTransformer(const std::string& annotation, TransformerType* t){ const unsigned int id = TransformerInfo::id; auto range = __subscriptions.equal_range(annotation); const auto entry = make_pair(annotation, id); __subscriptions.erase(std::find_if(range.first, range.second, [id](const auto& el){return el.second == id;})); __transformers.erase(id); } template TransformerType* update(TransformerType* newInstance){ const int id = TransformerInfo::id; Transformer* oldInstance = __transformers[id]; __transformers[id] = newInstance; return static_cast(oldInstance); } template bool exists(){ const int id = TransformerInfo::id; return __transformers.count(id); } template TransformerType* get(){ const int id = TransformerInfo::id; return static_cast(__transformers.at(id)); } private: std::map __transformers; std::multimap __subscriptions; }; +/**\brief Provides custom transformations during Code Scope compilation + * \extends xreate::compilation::ICodeScopeUnit + */ template class TransformationsScopeDecorator: public Transformer, public Parent { // SCOPE DECORATOR PART public: TransformationsScopeDecorator(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass) : Parent(codeScope, f, compilePass){} virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl=""){ llvm::Value* result = Parent::process(expr, hintVarDecl); return transform(expr, result, Context{this, Parent::function, Parent::pass}); } // TRANSFORMER PART public: virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx) { llvm::Value* result = raw; TransformationsManager* man = Parent::pass->managerTransformations; if (expression.tags.size()) for (Transformer* handler: man->getRelevantTransformers(expression)){ result = handler->transform(expression, result, ctx); } return result; } }; } } #endif /* TRANSFORMATIONS_H */ diff --git a/cpp/src/compilation/transformersaturation.cpp b/cpp/src/compilation/transformersaturation.cpp index 7f91e8c..053c1d9 100644 --- a/cpp/src/compilation/transformersaturation.cpp +++ b/cpp/src/compilation/transformersaturation.cpp @@ -1,80 +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/. - * + * * transformersaturation.cpp * * Author: pgess * Created on March 25, 2017, 10:06 PM */ +/** + * \file transformersaturation.h + * \brief Loop saturation support + */ + #include "transformersaturation.h" #include "llvmlayer.h" using namespace llvm; namespace xreate { namespace compilation { TransformerSaturation::TransformerSaturation(llvm::BasicBlock* allocationBlock, TransformationsManager* manager) : man(manager), blockAllocation(allocationBlock){ llvm::Type* tyInt1 = llvm::Type::getInt1Ty(llvm::getGlobalContext()); constTrue = llvm::ConstantInt::get(tyInt1, 1); constFalse = llvm::ConstantInt::get(tyInt1, 0); if (man->exists()){ oldInstance = man->update(this); } else { man->registerTransformer("break", this); } } TransformerSaturation::~TransformerSaturation(){ if (oldInstance) { man->update(oldInstance); } else { man->unregisterTransformer("break", this); } } llvm::Value* TransformerSaturation::transform(const Expression& expression, llvm::Value* raw, const Context& ctx){ processBreak(ctx); return raw; } void TransformerSaturation::processBreak(const Context& ctx){ allocateFlag(ctx); //show the saturation flag llvm::IRBuilder<>& builder = ctx.pass->man->llvm->builder; builder.CreateStore(constTrue, flagSaturation, true); } void TransformerSaturation::allocateFlag(const Context& ctx){ //allocation of saturation flag llvm::Type* tyInt1 = llvm::Type::getInt1Ty(llvm::getGlobalContext()); IRBuilder<> builder(blockAllocation, blockAllocation->getFirstInsertionPt()); flagSaturation = builder.CreateAlloca(tyInt1, constTrue, "flagSaturation"); builder.CreateStore(constFalse, flagSaturation, true); } bool TransformerSaturation::insertSaturationChecks(llvm::BasicBlock* blockContinue, llvm::BasicBlock* blockExit, const Context& ctx){ if (!flagSaturation) return false; llvm::IRBuilder<>& builder = ctx.pass->man->llvm->builder; builder.CreateCondBr(builder.CreateLoad(flagSaturation), blockExit, blockContinue); return true; } } } diff --git a/cpp/src/compilation/versions.h b/cpp/src/compilation/versions.h index ec9006b..6369d31 100644 --- a/cpp/src/compilation/versions.h +++ b/cpp/src/compilation/versions.h @@ -1,128 +1,139 @@ /* 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 + */ + #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 + * \extends xreate::compilation::ICodeScopeUnit + * \sa VersionsPass, VersionsGraph + */ template class VersionsScopeDecorator: public Parent{ typedef VersionsScopeDecorator SELF; public: VersionsScopeDecorator(CodeScope* 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=""){ llvm::IntegerType* tyInt = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::ConstantInt* constOne = llvm::ConstantInt::get(tyInt, 1, false); return compilation::ICodeScopeUnit::pass->man->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; } }; } } //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; // } //} //}; diff --git a/cpp/src/contextrule.cpp b/cpp/src/contextrule.cpp index 5904649..489af60 100644 --- a/cpp/src/contextrule.cpp +++ b/cpp/src/contextrule.cpp @@ -1,56 +1,61 @@ /* 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/. - * + * * contextrule.cpp * * Created on: Jan 2, 2016 * Author: pgess */ +/** + * \file contextrule.h + * \brief Context rules support. See more on [context rules](/w/concepts/context#context-rules) + */ + #include "contextrule.h" #include "clasplayer.h" #include "analysis/aux.h" #include using namespace xreate; using namespace std; ContextRule::ContextRule(const Expression& rule) { assert(rule.op == Operator::CONTEXT_RULE); assert(rule.operands.size() == 3); head = rule.operands.at(0); guards = rule.operands.at(1); body = rule.operands.at(2); } std::string ContextRule::compile(const ScopePacked& scopeId) const{ const string prolog = " %context rule visibility implemenetation\n" "context_rule_visibility(X, Y) :- X=Y, scope(X), scope(Y).\n" "context_rule_visibility(X, Y) :- cfa_parent(X, scope(Y)), scope(X), scope(Y).\n"; listrepHead_ = xreate::analysis::compile(head); assert(repHead_.size() == 1); listrepGuards_ = xreate::analysis::compile(guards); assert(repGuards_.size() == 1); listrepBody_ = xreate::analysis::compile(body); assert(repBody_.size() == 1); const std::string& atomBindingScope = Config::get("clasp.bindings.scope"); boost::format formatContextVisibility("context_rule_visibility(ScopeX, %1%)"); boost::format formatScopeBind(atomBindingScope + "(ScopeX, %1%, Linkage)"); const string& repHead = str(formatScopeBind % repHead_.front()); const string& repGuards = str(formatScopeBind % repGuards_.front()); const string& repVisibility = str(formatContextVisibility % scopeId); boost::format formatRule("%1%:- %2%, %3%, %4%."); return prolog + str(formatRule % repHead % repGuards % repBody_.front() % repVisibility); } diff --git a/cpp/src/llvmlayer.cpp b/cpp/src/llvmlayer.cpp index 2f404cf..b00afd2 100644 --- a/cpp/src/llvmlayer.cpp +++ b/cpp/src/llvmlayer.cpp @@ -1,266 +1,271 @@ /* 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 + */ + #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) : ast(root), builder(getGlobalContext()) { module = new llvm::Module(root->getModuleName(), llvm::getGlobalContext()); 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))); jit = builder .setEngineKind(llvm::EngineKind::JIT) .setErrorStr(&ErrStr) .setVerifyModules(true) .create(); } void LLVMLayer::print(){ llvm::PassManager PM; PM.addPass(llvm::PrintModulePass(llvm::outs(), "banner")); PM.run(*module); } llvm::BasicBlock* LLVMLayer::initExceptionBlock(llvm::BasicBlock* blockException){ initExceptionsSupport(); PointerType* tyInt8P = PointerType::getInt8PtrTy(llvm::getGlobalContext()); Value* nullInt8P = llvm::ConstantPointerNull::get(tyInt8P); builder.SetInsertPoint(blockException); llvm::Function* fAllocate = module->getFunction("__cxa_allocate_exception"); llvm::Function* fThrow = module->getFunction("__cxa_throw"); auto exception = builder.CreateCall(fAllocate, ConstantInt::get(IntegerType::getInt64Ty(getGlobalContext()), 4)); vector throwParams{exception, nullInt8P, nullInt8P}; builder.CreateCall(fThrow, ArrayRef(throwParams)); builder.CreateUnreachable(); return blockException; } 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::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::STRUCT: { assert(t.__operands.size()); 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(llvm::getGlobalContext(), pack, false); }; case TypeOperator::LINK: { llvm::StructType* conjuction = llvm::StructType::create(llvm::getGlobalContext()); 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); }; case TypeOperator::VARIANT: { int size = t.fields.size(); assert(size); int bitcount = ceil(log2(size)); return llvm::Type::getIntNTy(llvm::getGlobalContext(), bitcount); } case TypeOperator::NONE: { switch (t.__value) { case TypePrimitive::I32: case TypePrimitive::Int: case TypePrimitive::Num: return llvm::Type::getInt32Ty(llvm::getGlobalContext()); case TypePrimitive::Bool: return llvm::Type::getInt1Ty(llvm::getGlobalContext()); case TypePrimitive::I8: return llvm::Type::getInt8Ty(llvm::getGlobalContext()); case TypePrimitive::I64: return llvm::Type::getInt64Ty(llvm::getGlobalContext()); case TypePrimitive::Float: return llvm::Type::getDoubleTy(llvm::getGlobalContext()); case TypePrimitive::String: return llvm::Type::getInt8PtrTy(llvm::getGlobalContext()); default: assert(false); } } default: assert(false); } assert(false); return nullptr; } void LLVMLayer::initExceptionsSupport(){ Type* typInt8Ptr = PointerType::get(IntegerType::get(module->getContext(), 8), 0); if (!module->getFunction("__cxa_throw")) { std::vector fThrowSignature{typInt8Ptr, typInt8Ptr, typInt8Ptr}; FunctionType* tyFThrow = FunctionType::get( /*Result=*/Type::getVoidTy(module->getContext()), /*Params=*/fThrowSignature, /*isVarArg=*/false); llvm::Function::Create( /*Type=*/tyFThrow, /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"__cxa_throw", module); // (external, no body) } if (!module->getFunction("__cxa_allocate_exception")) { std::vectorfAllocateSignature{IntegerType::get(module->getContext(), 64)}; FunctionType* tyFAllocate = FunctionType::get( /*Result=*/typInt8Ptr, /*Params=*/fAllocateSignature, /*isVarArg=*/false); llvm::Function::Create( /*Type=*/tyFAllocate, /*Linkage=*/GlobalValue::ExternalLinkage, /*Name=*/"__cxa_allocate_exception", module); // (external, no body) } } bool TypeUtils::isStruct(const ExpandedType& ty){ const TypeAnnotation& t = ty.get(); if (t.__operator==TypeOperator::STRUCT) { 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::STRUCT) ? t.get().fields : llvm->layerExtern->getStructFields( llvm->layerExtern->lookupType(t.get().__valueCustom)); } diff --git a/cpp/src/modules.cpp b/cpp/src/modules.cpp index cb5d497..c7bf5aa 100644 --- a/cpp/src/modules.cpp +++ b/cpp/src/modules.cpp @@ -1,178 +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/. - * + * * modules.cpp * * Author: pgess * Created on July 22, 2017, 5:13 PM */ +/** + * \file modules.h + * \brief Modules support + */ + #include "modules.h" #include "modules/Parser.h" #include "analysis/aux.h" #include #include #include #include #include namespace fs = boost::filesystem; namespace xreate { namespace modules{ void ModuleRecord::addModuleQuery(const Expression& query){ __queries.push_back(query); } void ModuleRecord::addControllerPath(const std::string& path){ __controllers.push_back(path); } void ModuleRecord::addDiscoveryPath(const std::string& path){ __discoveries.push_back(path); } void ModuleRecord::addProperty(const Expression& prop){ __properties.push_back(prop); } void ModulesSolver::loadControllers(const ModuleRecord& module){ for (const std::string& pathController: module.__controllers){ std::fstream fileContent(pathController); __program << fileContent.rdbuf(); } } void ModulesSolver::extractProperties(const ModuleRecord& module){ unsigned int moduleId = __registry->getModuleHash(module.__path); const std::string atomProperty = "bind_module"; boost::format formatProperty(atomProperty + "(%1%, %2%)."); for (const Expression& property: module.__properties){ std::list reprProp = xreate::analysis::compile(property); assert(reprProp.size()== 1); __program << (formatProperty % moduleId % reprProp.front()) << std::endl; } } void ModulesSolver::discoverModules(const ModuleRecord& moduleClient){ std::regex extXreate("\\.xreate$", std::regex::basic); for(const std::string& path: moduleClient.__discoveries){ for(fs::directory_entry e: fs::recursive_directory_iterator(path)) { if (fs::is_regular_file(e.status())){ if (!std::regex_search(e.path().string(), extXreate)) continue; FILE* script = fopen(e.path().c_str(), "r"); Scanner scanner(script); Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Discovery errors"); parser.module.__path = e.path().c_str(); extractProperties(parser.module); fclose(script); } } } } void ModulesSolver::extractRequirements(const ModuleRecord& module){ const std::string atomQuery = "module_require"; boost::format formatProperty(atomQuery + "(%1%, %2%)."); unsigned int moduleId = __registry->getModuleHash(module.__path); for (const Expression& query: module.__queries){ std::list reprQuery = xreate::analysis::compile(query); assert(reprQuery.size()== 1); __program << (formatProperty % moduleId % reprQuery.front()) << std::endl; } } - void + ModulesSolver::add(const std::string& base){ __program << base; } void ModulesSolver::init(const std::string& programBase, const ModuleRecord& module){ add(programBase); extractRequirements(module); extractProperties(module); loadControllers(module); discoverModules(module); std::cout << __program.str() << std::endl; } std::list ModulesSolver::run(const ModuleRecord& module){ const std::string atomDecision = "module_include"; unsigned int moduleId = __registry->getModuleHash(module.__path); std::list result; 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); ctl.solve([&atomDecision, this, &result, moduleId](Gringo::Model const &model) { for (Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) { std::cout << atom << std::endl; if (std::strcmp(atom.name().c_str(), atomDecision.c_str())==0){ auto rowDecision = ClaspLayer::parse(atom); unsigned int moduleIdActual = std::get<0>(rowDecision); if (moduleIdActual == moduleId){ Gringo::Symbol moduleDecided = std::get<1>(rowDecision); switch (moduleDecided.type()) { case Gringo::SymbolType::Str: result.push_back(moduleDecided.string().c_str()); break; case Gringo::SymbolType::Num: result.push_back(__registry->getModuleNameByHash(moduleDecided.num())); break; default: assert(false && "Inappropriate symbol type"); } } } } return true; }, {}); return result; } const std::string& ModulesRegistry::getModuleNameByHash(unsigned int hash){ auto result = __registry.right.find(hash); assert(result != __registry.right.end()); return result->second; } unsigned int ModulesRegistry::getModuleHash(const std::string& moduleName){ auto result = __registry.left.insert(Hash::left_value_type(moduleName, __registry.size())); return result.first->second; } }} //namespace xreate::modules diff --git a/cpp/src/modules.h b/cpp/src/modules.h index 31a57db..6800cd2 100644 --- a/cpp/src/modules.h +++ b/cpp/src/modules.h @@ -1,79 +1,90 @@ /* 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{ + +/**\brief Cache of already processed modules */ class ModulesRegistry{ public: const std::string& getModuleNameByHash(unsigned int hash); unsigned int getModuleHash(const std::string& moduleName); private: typedef boost::bimap Hash; Hash __registry; }; class ModulesSolver; +/**\brief Holds information related to an individual module + * \sa XreateManagerDecoratorModules, ModulesSolver, ModulesRegistry + */ class ModuleRecord { FRIENDS_MODULES_TESTS friend class ModulesSolver; public: void addModuleQuery(const Expression& query); void addControllerPath(const std::string& path); void addDiscoveryPath(const std::string& path); void addProperty(const Expression& prop); private: std::list __queries; std::list __controllers; std::list __discoveries; std::list __properties; public: std::string __path; }; +/** \brief Resolves module's requrements + * \sa XreateManagerDecoratorModules, ModuleRecord + */ class ModulesSolver{ FRIENDS_MODULES_TESTS public: ModulesSolver(ModulesRegistry *registry): __registry(registry) {} + /** \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); private: ModulesRegistry* __registry; 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 64d2e50..387efbf 100644 --- a/cpp/src/pass/abstractpass.cpp +++ b/cpp/src/pass/abstractpass.cpp @@ -1,65 +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/. - * + * * 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 AbstractPassBase::finish(){} +void IPass::finish(){} -AbstractPassBase::AbstractPassBase(PassManager *manager) +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::getDeclaration(symbol); + const Expression& declaration = CodeScope::getDefinition(symbol); 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 + * + * \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::ClaspLayer 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. + * + * Template parameter `Output` specify 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::ClaspLayer solver + * + * + * \sa xreate::XreateManager, xreate::IPass, xreate::AST + */ + diff --git a/cpp/src/pass/abstractpass.h b/cpp/src/pass/abstractpass.h index 59bca6b..44808f5 100644 --- a/cpp/src/pass/abstractpass.h +++ b/cpp/src/pass/abstractpass.h @@ -1,201 +1,205 @@ /* 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*/ struct PassContext { CodeScope* scope = 0; ManagedFnPtr function; ManagedRulePtr rule; std::string varDecl; PassContext() {} PassContext updateScope(CodeScope* scopeNew) { PassContext context2{*this}; context2.scope = scopeNew; return context2; } ~PassContext(){} }; - class AbstractPassBase { + /** \brief Base class for all passes to inherit */ + class IPass { public: - AbstractPassBase(PassManager* manager); + IPass(PassManager* manager); + + /** \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 */ 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 */ 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*/ template -class AbstractPass: public AbstractPassBase { - +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::getDeclaration(symbol); + const Expression& declaration = CodeScope::getDefinition(symbol); 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->getFunctionVariants(calleeName); if (callees.size() == 1 && callees.front()){ return processFnCall(callees.front(), context); } else { for (const ManagedFnPtr& callee: callees){ processFnCallUncertain(callee, context); } return defaultValue(); } } SymbolCache& getSymbolCache(){ return __visitedSymbols; } - - public: AbstractPass(PassManager* manager) - : AbstractPassBase(manager){} + : IPass(manager){} - virtual Output processFnCall(ManagedFnPtr function, PassContext context){ + /** \brief Processes function invocation instruction */ + virtual Output processFnCall(ManagedFnPtr functionCallee, PassContext context){ return defaultValue(); } - virtual void processFnCallUncertain(ManagedFnPtr function, PassContext context) + /** \brief Processes function invocation instruction in uncertain cases + * \details Executed when it's impossible statically determine exact function invocation. + * In this case get executed for all possible candidates + */ + virtual void processFnCallUncertain(ManagedFnPtr functionCallee, PassContext context) {} + /** \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); } + /** \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 - -//PROCESS FUNCTION: -// const Symbol& symbolFunction{0, function->getEntryScope()}; -// -// if (__visitedSymbols.isCached(symbolFunction)) -// return __visitedSymbols.getCachedValue(symbolFunction); -// -// PassContext context; -// context.function = function; -// -// Output&& result = process(function->getEntryScope(), context); -// return __visitedSymbols.setCachedValue(symbolFunction, std::move(result)); diff --git a/cpp/src/pass/cfapass.cpp b/cpp/src/pass/cfapass.cpp index 72cdbfa..cf2a829 100644 --- a/cpp/src/pass/cfapass.cpp +++ b/cpp/src/pass/cfapass.cpp @@ -1,109 +1,119 @@ /* 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 "cfapass.h" #include "analysis/cfagraph.h" #include using namespace std; using namespace xreate::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->clasp->setCFAData(move(__context.graph)); return AbstractPass::finish(); } void CFAPass::processFnCall(ManagedFnPtr function, PassContext context) { ClaspLayer* clasp = man->clasp; __context.graph->addCallConnection(clasp->pack(context.scope), function->getName()); return AbstractPass::processFnCall(function, context); } void CFAPass::processFnCallUncertain(ManagedFnPtr function, PassContext context){ ClaspLayer* clasp = man->clasp; __context.graph->addCallConnection(clasp->pack(context.scope), function->getName()); return AbstractPass::processFnCallUncertain(function, context); } void CFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){ ClaspLayer* clasp = man->clasp; CodeScope* scopeParent = context.scope; ScopePacked scopeId = clasp->pack(scope); if (scopeParent){ __context.graph->addParentConnection(scopeId, clasp->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){ ClaspLayer* clasp = man->clasp; 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(clasp->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->clasp)} {} + +/** + * \class xreate::cfa::CFAPass + * \details Provides CFA, important analysis for reasoning. Iterates over AST and stores collected data in CFAGraph + */ \ No newline at end of file diff --git a/cpp/src/pass/cfapass.h b/cpp/src/pass/cfapass.h index 8704523..7e12062 100644 --- a/cpp/src/pass/cfapass.h +++ b/cpp/src/pass/cfapass.h @@ -1,48 +1,49 @@ /* 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 "clasplayer.h" #include "abstractpass.h" namespace xreate{namespace cfa { class CFAGraph; +/** \brief Control Flow Analysis Pass(%CFA)*/ class CFAPass : public AbstractPass { public: void process(ManagedFnPtr function) override; void processFnCall(ManagedFnPtr function, PassContext context) override; void processFnCallUncertain(ManagedFnPtr function, 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; private: 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/compilepass.cpp b/cpp/src/pass/compilepass.cpp index a30e97d..c715726 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,800 +1,835 @@ /* 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 + */ + #include "compilepass.h" #include "clasplayer.h" #include #include "llvmlayer.h" #include "query/containers.h" #include "query/context.h" #include "compilation/containers.h" #include "compilation/latecontextcompiler2.h" #include "ExternLayer.h" #include "pass/adhocpass.h" #include "compilation/targetinterpretation.h" #include "pass/versionspass.h" #include "compilation/scopedecorators.h" #include "compilation/adhocfunctiondecorator.h" #include "compilation/operators.h" #include "analysis/typeinference.h" #include #include #include using namespace std; using namespace llvm; //TODO use Scope //SECTIONTAG late-context FunctionDecorator namespace xreate{namespace context{ + +/** \brief Late Context enabled decorator for IFunctionUnit + * \extends IFunctionUnit + */ template class LateContextFunctionDecorator : public Parent { public: LateContextFunctionDecorator(ManagedFnPtr f, CompilePass* p) : Parent(f, p), contextCompiler(this, p) { } protected: std::vector prepareArguments() { std::vector&& arguments = Parent::prepareArguments(); size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize(); if (sizeLateContextDemand) { llvm::Type* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::Type* tyDemand = llvm::ArrayType::get(ty32, sizeLateContextDemand); arguments.push_back(tyDemand); } return arguments; } llvm::Function::arg_iterator prepareBindings() { llvm::Function::arg_iterator fargsI = Parent::prepareBindings(); size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize(); if (sizeLateContextDemand) { fargsI->setName("latecontext"); contextCompiler.rawContextArgument = &*fargsI; ++fargsI; } return fargsI; } public: context::LateContextCompiler2 contextCompiler; }; }} //end of namespace xreate::context namespace xreate { namespace compilation{ std::string BasicFunctionUnit::prepareName(){ AST* ast = IFunctionUnit::pass->man->root; string name = ast->getFunctionVariants(IFunctionUnit::function->__name).size() > 1 ? IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : IFunctionUnit::function->__name; return name; } std::vector BasicFunctionUnit::prepareArguments() { 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 context::LateContextFunctionDecorator< adhoc::AdhocFunctionDecorator< BasicFunctionUnit>> DefaultFunctionUnit; ICodeScopeUnit::ICodeScopeUnit(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass) : pass(compilePass), function(f), scope(codeScope) { } llvm::Value* CallStatementRaw::operator()(std::vector&& args, const std::string& hintDecl) { llvm::Function* calleeInfo = dyn_cast(__callee); if (calleeInfo) { auto argsFormal = calleeInfo->args(); int pos = 0; //SECTIONTAG types/convert function ret value for (auto argFormal = argsFormal.begin(); argFormal != argsFormal.end(); ++argFormal, ++pos) { args[pos] = typeinference::doAutomaticTypeConversion(args[pos], argFormal->getType(), llvm->builder); } } return llvm->builder.CreateCall(__calleeTy, __callee, args, hintDecl); } //DESABLEDFEATURE implement inlining -class CallStatementInline : public CallStatement { +class CallStatementInline : public ICallStatement { 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(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass) : ICodeScopeUnit(codeScope, f, compilePass) { } llvm::Value* BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { - Expression declaration = CodeScope::getDeclaration(s); + Expression declaration = CodeScope::getDefinition(s); CodeScope* scope = s.scope; ICodeScopeUnit* self = ICodeScopeUnit::function->getScopeUnit(scope); return self->process(declaration, hintRetVar); } -//SECTIONTAG late-context find callee function +//TASK Isolate out context functionalty in decorator //TOTEST static late context decisions //TOTEST dynamic late context decisions -CallStatement* +ICallStatement* BasicCodeScopeUnit::findFunction(const std::string& calleeName) { LLVMLayer* llvm = pass->man->llvm; ClaspLayer* clasp = pass->man->clasp; DefaultFunctionUnit* function = dynamic_cast (this->function); context::ContextQuery* queryContext = pass->queryContext; const std::list& specializations = pass->man->root->getFunctionVariants(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 CallStatementRaw(external, llvm); } //no decisions required if (specializations.size() == 1) { if (!specializations.front()->guardContext.isValid()) { return new CallStatementRaw(pass->getFunctionUnit(specializations.front())->compile(), llvm); } } //TODO move dictSpecialization over to a separate function in order to perform cache, etc. //prepare specializations dictionary std::map dictSpecializations; boost::optional variantDefault; boost::optional variant; for (const ManagedFnPtr& f : specializations) { const Expression& guard = f->guardContext; //default case: if (!guard.isValid()) { variantDefault = f; continue; } assert(dictSpecializations.emplace(guard, f).second && "Found several identical specializations"); } //check static context ScopePacked scopeCaller = clasp->pack(this->scope); const string atomSpecialization = "specialization"; const Expression topicSpecialization(Operator::CALL,{(Atom(string(atomSpecialization))), Expression(Operator::CALL, {Atom(string(calleeName))}), Atom(scopeCaller)}); const context::Decisions& decisions = queryContext->getFinalDecisions(scopeCaller); if (decisions.count(topicSpecialization)) { variant = dictSpecializations.at(decisions.at(topicSpecialization)); } //TODO check only demand for this particular topic. size_t sizeDemand = function->contextCompiler.getFunctionDemandSize(); //decision made if static context found or no late context exists(and there is default variant) bool flagHasStaticDecision = variant || (variantDefault && !sizeDemand); //if no late context exists if (flagHasStaticDecision) { IFunctionUnit* calleeUnit = pass->getFunctionUnit(variant ? *variant : *variantDefault); //inlining possible based on static decision only // if (calleeUnit->isInline()) { // return new CallStatementInline(function, calleeUnit); // } return new CallStatementRaw(calleeUnit->compile(), llvm); } //require default variant if no static decision made assert(variantDefault); llvm::Function* functionVariantDefault = this->pass->getFunctionUnit(*variantDefault)->compile(); llvm::Value* resultFn = function->contextCompiler.findFunction(calleeName, functionVariantDefault, scopeCaller); llvm::PointerType *resultPTy = cast(resultFn->getType()); llvm::FunctionType *resultFTy = cast(resultPTy->getElementType()); return new CallStatementRaw(resultFn, resultFTy, 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::Advanced instructions = xreate::compilation::Advanced({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]); //SECTIONTAG types/convert binary operation right = typeinference::doAutomaticTypeConversion(right, left->getType(), l.builder); 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: return l.builder.CreateSDiv(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")); 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); std::string nameCallee = expr.getValueString(); - shared_ptr callee(findFunction(nameCallee)); + shared_ptr callee(findFunction(nameCallee)); //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); } ); ScopePacked outerScopeId = pass->man->clasp->pack(this->scope); //TASK a) refactor CALL/ADHOC/find function //SECTIONTAG late-context propagation arg size_t calleeDemandSize = pass->queryContext->getFunctionDemand(nameCallee).size(); if (calleeDemandSize) { DefaultFunctionUnit* function = dynamic_cast (this->function); llvm::Value* argLateContext = function->contextCompiler.compileContextArgument(nameCallee, outerScopeId); args.push_back(argLateContext); } 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::LOOP_CONTEXT: { assert(false); return nullptr; //return instructions.compileLoopContext(expr, DEFAULT("tmp_loop")); } case Operator::LOGIC_AND: { assert(expr.operands.size() == 1); return process(expr.operands[0]); } case Operator::LIST: { return instructions.compileListAsSolidArray(expr, DEFAULT("tmp_list")); }; case Operator::LIST_RANGE: { assert(false); //no compilation phase for a range list // return InstructionList(this).compileConstantArray(expr, l, hintRetVar); }; case Operator::LIST_NAMED: { typedef Expanded ExpandedType; ExpandedType tyStructLiteral = l.ast->getType(expr); const std::vector fieldsFormal = (tyStructLiteral.get().__operator == TypeOperator::CUSTOM) ? l.layerExtern->getStructFields(l.layerExtern->lookupType(tyStructLiteral.get().__valueCustom)) : tyStructLiteral.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(tyStructLiteral)); 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 = 0; //TODO Null ad hoc llvm implementation (related code: operators/StructUpdate/add) // if (operand.isNone()){ // llvm::Type* tyNullField = tyRecord->getElementType(fieldId); // result = llvm::UndefValue::get(tyNullField); // // } else { result = process(operand); // } assert(result); record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef({fieldId})); } return record; }; case Operator::MAP: { assert(expr.blocks.size()); return instructions.compileMapSolidOutput(expr, DEFAULT("map")); }; case Operator::FOLD: { return instructions.compileFold(expr, DEFAULT("fold")); }; case Operator::FOLD_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::STRUCT: 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::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); } }; //SECTIONTAG adhoc actual compilation //TODO a) make sure that it's correct: function->adhocImplementation built for Entry scope and used in another scope case Operator::ADHOC: { DefaultFunctionUnit* function = dynamic_cast (this->function); assert(function->adhocImplementation && "Adhoc implementation not found"); const Expression& comm = adhoc::AdhocExpression(expr).getCommand(); CodeScope* scope = function->adhocImplementation->getCommandImplementation(comm); ICodeScopeUnit* unitScope = function->getScopeUnit(scope); //SECTIONTAG types/convert ADHOC ret convertation llvm::Type* resultTy = l.toLLVMType(pass->man->root->expandType(function->adhocImplementation->getResultType())); return typeinference::doAutomaticTypeConversion(unitScope->compile(), resultTy, l.builder); }; 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::VARIANT: { //TASK Variant compilation to implement assert(false&& "Variant compilation not implemented yet"); return nullptr; // const ExpandedType& typVariant = pass->man->root->getType(expr); // llvm::Type* typRaw = l.toLLVMType(typVariant); // int value = expr.getValueDouble(); // return llvm::ConstantInt::get(typRaw, value); } case Operator::NONE: 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; if (expr.type.isValid()) { typConst = l.toLLVMType(pass->man->root->getType(expr)); } else { typConst = llvm::Type::getInt32Ty(llvm::getGlobalContext()); } int literal = expr.getValueDouble(); return llvm::ConstantInt::get(typConst, 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) { if (!hintBlockDecl.empty()) { llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm::getGlobalContext(), hintBlockDecl, function->raw); pass->man->llvm->builder.SetInsertPoint(block); } 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 = prepareArguments(); 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(CodeScope* 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 DefaultFunctionUnit(function, this); } template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators::buildCodeScopeUnit(CodeScope* scope, IFunctionUnit* function){ return new DefaultCodeScopeUnit(scope, function, this); } } // emf of compilation IFunctionUnit* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { IFunctionUnit* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } void CompilePass::run() { managerTransformations = new TransformationsManager(); targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); queryContext = reinterpret_cast (man->clasp->getQuery(QueryId::ContextQuery)); //Find out main function; ClaspLayer::ModelFragment model = man->clasp->query(Config::get("function-entry")); assert(model && "Error: No entry function found"); assert(model->first != model->second && "Error: Ambiguous entry function"); string nameMain = std::get<0>(ClaspLayer::parse(model->first->second)); IFunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain)); entry = unitMain->compile(); } llvm::Function* CompilePass::getEntryFunction() { assert(entry); return entry; } void CompilePass::prepareQueries(ClaspLayer* clasp) { clasp->registerQuery(new containers::Query(), QueryId::ContainersQuery); clasp->registerQuery(new context::ContextQuery(), QueryId::ContextQuery); } } //end of namespace xreate + +/** + * \class xreate::CompilePass + * \brief Encapsulates all compilation activities + * + * 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:ClaspLayer 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) + * + * \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 + * - Targets to allow more versitile extensions. + * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See xreate::compilation::Target + * - %Altering Function invocation. xreate::compilation::ICallStatement + * + * Client able to construct compiler with desired decorators using xreate::compilation::CompilePassCustomDecorators. + * As a handy alias, `CompilePassCustomDecorators` constructs default compiler + * + */ diff --git a/cpp/src/pass/compilepass.h b/cpp/src/pass/compilepass.h index 08112b7..d826fb4 100644 --- a/cpp/src/pass/compilepass.h +++ b/cpp/src/pass/compilepass.h @@ -1,192 +1,216 @@ /* 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 ClaspLayer; class CompilePass; class LLVMLayer; namespace adhoc{ class AdhocScheme; } namespace context{ class ContextQuery; class LateContextCompiler2; } 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; }; -class CallStatement { +/** \brief Interface to specify custom way of function invocation + * \details Default implementation is xreate::compilation::CallStatementRaw + */ +class ICallStatement { public: + /** \brief Returns result of custom function invocation for given arguments*/ virtual llvm::Value* operator() (std::vector&& args, const std::string& hintDecl="") = 0; }; -class CallStatementRaw: public CallStatement{ +/** \brief Default ICallStatement implementation */ +class CallStatementRaw: public ICallStatement{ public: CallStatementRaw(llvm::Function* callee, LLVMLayer* l) : __callee(callee), __calleeTy(callee->getFunctionType()), llvm(l) {} CallStatementRaw(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=""); private: llvm::Value* __callee; llvm::FunctionType* __calleeTy; - LLVMLayer* llvm; }; +/** \brief Interface to allow modification of CodeScope compilation */ class ICodeScopeUnit{ public: CompilePass* const pass; IFunctionUnit* const function; CodeScope* const scope; ICodeScopeUnit(CodeScope* 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 void bindArg(llvm::Value* value, std::string&& alias)=0; virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0; protected: - virtual CallStatement* findFunction(const std::string& callee)=0; + virtual ICallStatement* findFunction(const std::string& callee)=0; }; +/** \brief Minimal useful ICodeScopeUnit implementation suited for inheritance */ class BasicCodeScopeUnit: public ICodeScopeUnit{ public: BasicCodeScopeUnit(CodeScope* 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: - CallStatement* findFunction(const std::string& callee) override; + ICallStatement* findFunction(const std::string& callee) 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(CodeScope* scope); ICodeScopeUnit* getScopeUnit(ManagedScpPtr scope); ManagedFnPtr function; llvm::Function* raw = nullptr; protected: CompilePass* pass=nullptr; virtual std::string prepareName() = 0; virtual std::vector prepareArguments() = 0; virtual llvm::Type* prepareResult() = 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 prepareArguments() override; virtual llvm::Type* prepareResult() override; virtual llvm::Function::arg_iterator prepareBindings() override; }; } // end of namespace compilation class CompilePass : public AbstractPass { friend class context::LateContextCompiler2; friend class compilation::BasicCodeScopeUnit; friend class compilation::IFunctionUnit; public: compilation::TransformationsManager* managerTransformations; interpretation::TargetInterpretation* targetInterpretation; CompilePass(PassManager* manager): AbstractPass(manager) {} - compilation::IFunctionUnit* getFunctionUnit(const ManagedFnPtr& function); + /** \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::ClaspLayer */ static void prepareQueries(ClaspLayer* clasp); -public: + + +protected: virtual compilation::IFunctionUnit* buildFunctionUnit(const ManagedFnPtr& function)=0; virtual compilation::ICodeScopeUnit* buildCodeScopeUnit(CodeScope* scope, compilation::IFunctionUnit* function)=0; private: //TODO free `functions` in destructor std::map functions; llvm::Function* entry = 0; context::ContextQuery* queryContext; }; 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(CodeScope* scope, IFunctionUnit* function) override{ return new SCOPE_DECORATOR(scope, function, this); } }; template<> compilation::IFunctionUnit* CompilePassCustomDecorators::buildFunctionUnit(const ManagedFnPtr& function); template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators::buildCodeScopeUnit(CodeScope* 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 749ec9a..2186964 100644 --- a/cpp/src/pass/dfapass.cpp +++ b/cpp/src/pass/dfapass.cpp @@ -1,271 +1,238 @@ /* 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) + */ + +//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 "analysis/dfagraph.h" #include "xreatemanager.h" #include "clasplayer.h" #include using namespace std; namespace xreate { namespace dfa { class DfaExpressionProcessor { std::vector operands; std::vector blocks; const Expression expression; SymbolNode result; DFAPass * const pass; const PassContext context; public: DfaExpressionProcessor(const Expression& expr, SymbolNode resInitial, DFAPass * const p, const PassContext c) : expression(expr), result(resInitial), pass(p), context(c) { operands.reserve(expression.getOperands().size()); for (const Expression &op : expression.getOperands()) { SymbolAnonymous symbOp(op.id); operands.push_back(DfaExpressionProcessor(op, symbOp, pass, context).process()); } blocks.reserve(expression.blocks.size()); for (CodeScope* scope : expression.blocks) { blocks.push_back(pass->process(scope, context)); } } SymbolNode process() { if (expression.__state == Expression::COMPOUND) { processCompoundOp(); } else { processElementaryOp(); } applySignatureAnnotations(); applyInPlaceAnnotations(); return result; } private: void processElementaryOp() { switch (expression.__state) { case Expression::IDENT: { SymbolPacked symbFrom = pass->processSymbol(Attachments::get(expression), context, expression.getValueString()); SymbolPacked* symbTo = boost::get(&result); if (symbTo) { pass->__context.graph->addConnection(*symbTo, SymbolNode(symbFrom), DFGConnection::STRONG); } else { result = SymbolNode(symbFrom); } break; } default: break; } } void processCompoundOp() { switch (expression.op) { //DEBT provide CALL processing // case Operator::CALL: { // const string &nameCalleeFunction = expression.getValueString(); // // //TODO implement processFnCall/Uncertain // list variantsCalleeFunction = man->root->getFunctionVariants(nameCalleeFunction); // if (variantsCalleeFunction.size()!=1) return; // ManagedFnPtr function= variantsCalleeFunction.front(); // // // set calling relations: // CodeScope *scopeRemote = function->getEntryScope(); // std::vector::iterator nodeActual = cache.operands.begin(); // for (const std::string &identFormal: scopeRemote->__bindings){ // const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE}; // // __context.graph->addConnection(clasp->pack(Symbol{symbolFormal, scopeRemote}, nameCalleeFunction + ":" + identFormal), *nodeActual, DFGConnection::WEAK); // ++nodeActual; // } // // //TODO add RET connection // break; // } //MAP processing: apply PROTOTYPE relation case Operator::MAP: { SymbolNode nodeFrom = operands.front(); SymbolPacked* nodeTo = boost::get(&result); assert(nodeTo); pass->__context.graph->addConnection(*nodeTo, nodeFrom, DFGConnection::PROTOTYPE); break; } default: break; } } void applySignatureAnnotations() { if (pass->__signatures.count(expression.op)) { const Expression &scheme = pass->__signatures.at(expression.op); std::vector::iterator arg = operands.begin(); std::vector::const_iterator tag = scheme.getOperands().begin(); //Assign scheme RET annotation Expression retTag = *scheme.getOperands().begin(); if (retTag.__state != Expression::INVALID) { pass->__context.graph->addAnnotation(result, move(retTag)); } ++tag; while (tag != scheme.getOperands().end()) { if (tag->__state != Expression::INVALID) { pass->__context.graph->addAnnotation(*arg, Expression(*tag)); } ++arg; ++tag; } // TODO add possibility to have specific signature for a particular function // if (expression.op == Operator::CALL || expression.op == Operator::INDEX){ // string caption = expression.getValueString(); // operands.push_back(process(Expression(move(caption)), context, "")); // } } } void applyInPlaceAnnotations() { // write down in-place expression tags: for (pair tag : expression.tags) { pass->__context.graph->addAnnotation(result, Expression(tag.second)); } } }; DFAPass::DFAPass(PassManager* manager) : AbstractPass(manager) , __context{new DFAGraph(manager->clasp)} , clasp(manager->clasp) {} SymbolPacked DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl) { const SymbolPacked& symbRet = AbstractPass::process(scope, context, hintBlockDecl); return symbRet; } SymbolPacked DFAPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol) { - const Expression& declaration = CodeScope::getDeclaration(symbol); + const Expression& declaration = CodeScope::getDefinition(symbol); const SymbolPacked& symbPacked = clasp->pack(symbol, hintSymbol); DfaExpressionProcessor(declaration, symbPacked, this, context).process(); return symbPacked; } void DFAPass::run() { init(); return AbstractPass::run(); } void DFAPass::init() { for (const Expression& scheme : man->root->__dfadata) { __signatures.emplace(scheme.op, scheme); } } void DFAPass::finish() { clasp->setDFAData(move(__context.graph)); } } //end of namespace dfa template<> SymbolPacked defaultValue(){ assert(false); } } //xreate namespace - - - //DEBT represent VersionaPass in declarative form using applyDependencies - // applyDependencies(expression, context, cache, decl); - - //DEBT prepare static annotations and represent InterpretationPass in declarative form - // applyStaticAnnotations(expression, context, cache, decl); - - - - //TODO Null ad hoc DFG implementation/None symbol - //DISABLEDFEATURE None value - // if (expression.isNone()){ - // return SymbolTransient{{Atom(Config::get("clasp.nonevalue"))}}; - // } - - // non initialized(SymbolInvalid) value - - //void - //DFAPass::applyDependencies(const Expression& expression, PassContext context, ExpressionCache& cache, const std::string& decl){ - // for (SymbolNode &op: cache.operands) { - // __context.graph->addDependencyConnection(cache.result, op); - // } - // - // for (SymbolNode &block: cache.blocks) { - // __context.graph->addDependencyConnection(cache.result, block); - // } - // - // switch(expression.__state) { - // case Expression::IDENT: { - // SymbolNode identSymbol = clasp->pack(Attachments::get(expression), context.function->getName() + ":" + expression.getValueString()); - // __context.graph->addDependencyConnection(cache.result, identSymbol); - // } - // - // default: break; - // } - //} - - //void - //DFAPass::applyStaticAnnotations(const Expression& expression, PassContext context, ExpressionCache& cache, const std::string& decl){ - // - // switch(expression.__state) { - // case Expression::NUMBER: - // case Expression::STRING: - // __context.graph->addAnnotation(cache.result, Expression(Atom("static"))); - // break; - // - // default: break; - // } - //} +/** + * \class xreate::dfa::DFAPass + * \details Provides DFA, important analysis for reasoning. Iterates over AST and stores collected data in DFAGraph + */ diff --git a/cpp/src/pass/dfapass.h b/cpp/src/pass/dfapass.h index 926e9f3..2d4bd14 100644 --- a/cpp/src/pass/dfapass.h +++ b/cpp/src/pass/dfapass.h @@ -1,49 +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 * * dfapass.h * Data Flow Graph building pass */ #ifndef DFGPASS_H #define DFGPASS_H #include "abstractpass.h" #include "analysis/dfagraph.h" namespace xreate { class ClaspLayer; } namespace xreate { namespace dfa { class DfaExpressionProcessor; +/** \brief Data Flow Analysis Pass(%DFA) */ class DFAPass: public AbstractPass { friend class DfaExpressionProcessor; public: DFAPass(PassManager* manager); SymbolPacked processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol="") override; SymbolPacked process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl="") override; void init(); void run() override; void finish() override; private: struct { DFAGraph* graph; } __context; std::map __signatures; //DFA data for particular operators ClaspLayer* clasp; }; }} //end of xreate::dfa namespace #endif diff --git a/cpp/src/pass/interpretationpass.cpp b/cpp/src/pass/interpretationpass.cpp index c64f198..33773d3 100644 --- a/cpp/src/pass/interpretationpass.cpp +++ b/cpp/src/pass/interpretationpass.cpp @@ -1,461 +1,522 @@ /* 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 + */ + #include "pass/interpretationpass.h" #include #include #include "ast.h" //DEBT implement InterpretationPass purely in clasp //DEBT represent InterpretationPass as general type inference using namespace std; -namespace xreate{ +namespace xreate { template<> interpretation::InterpretationResolution -defaultValue(){ +defaultValue() { return interpretation::CMPL_ONLY; } -namespace interpretation{ -enum InterpretationQuery{QUERY_INTR_ONLY, QUERY_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("interpretation"); - if (i== tags.end()){ - return ANY; - } +template +bool +checkConstraints(InterpretationResolution flag) { + return( (flag==INTR_ONLY&&FLAG_REQUIRED==QUERY_INTR_ONLY) + ||(flag==CMPL_ONLY&&FLAG_REQUIRED==QUERY_CMPL_ONLY)); +} - assert(i->second.op == Operator::CALL); - const string& cmd = i->second.operands.at(0).getValueString(); +InterpretationResolution +recognizeTags(const map& tags) { + auto i=tags.find("interpretation"); + if(i==tags.end()){ + return ANY; + } - //TODO make consistent names of annotation and resolution - if (cmd == "force"){ - return INTR_ONLY; + assert(i->second.op==Operator::CALL); + const string& cmd=i->second.operands.at(0).getValueString(); - } else if (cmd == "suppress"){ - return CMPL_ONLY; - } + //TODO make consistent names of annotation and resolution + if(cmd=="force"){ + return INTR_ONLY; - return ANY; + } else if(cmd=="suppress"){ + 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){ + if(flagA==ANY){ return unify(flagB, flags...); } - if (flagB == ANY) { + if(flagB==ANY){ return unify(flagA, flags...); } - assert(flagA == flagB); + assert(flagA==flagB); return flagA; } template -bool checkConstraints(std::vector&& flags) { +bool +checkConstraints(std::vector&& flags) { assert(flags.size()); - InterpretationResolution flag = flags.front(); + InterpretationResolution flag=flags.front(); return details::checkConstraints(flag); } template -bool checkConstraints(std::vector&& flags) { +bool +checkConstraints(std::vector&& flags) { assert(flags.size()); - InterpretationResolution flag = flags.front(); + InterpretationResolution flag=flags.front(); flags.pop_back(); - if (details::checkConstraints(flag)){ + if(details::checkConstraints(flag)){ return checkConstraints(move(flags)); } return false; } bool -InterpretationData::isDefault() const{ - return (resolution == ANY && op == NONE); +InterpretationData::isDefault() const { + return(resolution==ANY&&op==NONE); } void -recognizeTags(const Expression& e){ +recognizeTags(const Expression& e) { InterpretationData tag{details::recognizeTags(e.tags), NONE}; - if (!tag.isDefault()) + if(!tag.isDefault()) Attachments::put(e, tag); } InterpretationResolution -recognizeTags(const ManagedFnPtr& f){ +recognizeTags(const ManagedFnPtr& f) { return details::recognizeTags(f->getTags()); } InterpretationPass::InterpretationPass(PassManager* manager) - : AbstractPass(manager) { +: AbstractPass(manager) { Attachments::init(); Attachments::init(); } -void InterpretationPass::run(){ - ManagedFnPtr f = man->root->begin(); - auto& visitedSymbols = getSymbolCache(); +void +InterpretationPass::run() { + ManagedFnPtr f=man->root->begin(); + auto& visitedSymbols=getSymbolCache(); - while (f.isValid()) { - const Symbol& symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; + while(f.isValid()) { + const Symbol&symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; - if (!visitedSymbols.isCached(symbolFunction)){ + if(!visitedSymbols.isCached(symbolFunction)){ visitedSymbols.setCachedValue(symbolFunction, process(f)); } ++f; } } InterpretationResolution -InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl){ +InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl) { recognizeTags(expression); - InterpretationResolution resolution = ANY; - InterpretationOperator op = NONE; + InterpretationResolution resolution=ANY; + InterpretationOperator op=NONE; - switch (expression.__state){ + switch(expression.__state) { - case Expression::NUMBER: - case Expression::STRING: { - break; - } + case Expression::NUMBER: + case Expression::STRING: + { + break; + } - case Expression::IDENT: { - resolution = Parent::processSymbol(Attachments::get(expression), context); - break; - } + case Expression::IDENT: + { + resolution=Parent::processSymbol(Attachments::get(expression), context); + break; + } - case Expression::COMPOUND: - break; + case Expression::COMPOUND: + break; - default: { resolution = CMPL_ONLY; break;} + default: + { + resolution=CMPL_ONLY; + break; + } } - if (expression.__state == Expression::COMPOUND) - switch(expression.op){ + 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); + case Operator::NE: + { + InterpretationResolution left=process(expression.operands[0], context); + InterpretationResolution right=process(expression.operands[1], context); - resolution = unify(left, right); + resolution=unify(left, right); break; } - case Operator::LOGIC_AND: { - assert(expression.operands.size() == 1); - resolution = process (expression.operands[0], context); + case Operator::LOGIC_AND: + { + assert(expression.operands.size()==1); + resolution=process(expression.operands[0], context); break; } - case Operator::CALL: { + case Operator::CALL: + { //TODO cope with static/dynamic context //TODO BUG here: if several variants they all are processed as CMPL careless of signature - list callees = man->root->getFunctionVariants(expression.getValueString()); - if (callees.size()!=1){ - resolution = CMPL_ONLY; + list callees=man->root->getFunctionVariants(expression.getValueString()); + if(callees.size()!=1){ + resolution=CMPL_ONLY; break; } - ManagedFnPtr callee = callees.front(); - const Symbol& symbCalleeFunc{ScopedSymbol::RetSymbol, callee->getEntryScope()}; + 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"); + 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); + resolution=unify(resolution, resCallee); } //check arguments compatibility - const FunctionInterpretationData& calleeSignature = FunctionInterpretationHelper::getSignature(callee); - for (size_t op=0, size = expression.operands.size(); op < size; ++op){ - const Expression &operand = expression.operands[op]; - InterpretationResolution argActual = process(operand, context); - InterpretationResolution argExpected = calleeSignature.signature[op]; + const FunctionInterpretationData& calleeSignature=FunctionInterpretationHelper::getSignature(callee); + for(size_t op=0, size=expression.operands.size(); op({flagCondition})){ - op= IF_INTERPRET_CONDITION; - flagCondition = ANY; + if(checkConstraints({flagCondition})){ + op=IF_INTERPRET_CONDITION; + flagCondition=ANY; } - resolution = unify(flagCondition, flagScope1, flagScope2); + resolution=unify(flagCondition, flagScope1, flagScope2); break; } - case Operator::FOLD: { - InterpretationResolution flagInput = process(expression.getOperands()[0], context); - InterpretationResolution flagAccumInit = process(expression.getOperands()[1], context); + 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}; + 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}; + 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); + InterpretationResolution flagBody=Parent::process(expression.blocks.front(), context); //special case: FOLD_INTERPRET_INPUT - if (checkConstraints({flagInput})){ - op= FOLD_INTERPRET_INPUT; - flagInput = ANY; + if(checkConstraints({flagInput})){ + op=FOLD_INTERPRET_INPUT; + flagInput=ANY; } - resolution = unify(flagInput, flagAccumInit, flagBody); + resolution=unify(flagInput, flagAccumInit, flagBody); break; } - case Operator::INDEX: { - for (const Expression &op: expression.getOperands()) { - resolution = unify(resolution, process(op, context)); + case Operator::INDEX: + { + for(const Expression &op : expression.getOperands()) { + resolution=unify(resolution, process(op, context)); } break; } - case Operator::SWITCH: { - InterpretationResolution flagCondition = process(expression.operands[0], context); - bool hasDefaultCase = expression.operands[1].op == Operator::CASE_DEFAULT; + 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})){ - op= SWITCH_INTERPRET_CONDITION; - flagHeaders = ANY; + if(checkConstraints({flagHeaders})){ + op=SWITCH_INTERPRET_CONDITION; + flagHeaders=ANY; } //determine body resolutions - resolution = flagHeaders; - for (size_t size = expression.operands.size(), i= 1; i({resolution})){ - op= SWITCH_VARIANT; - resolution = ANY; + if(checkConstraints({resolution})){ + op=SWITCH_VARIANT; + resolution=ANY; } - const string identCondition = expression.bindings.front(); - for(auto scope: expression.blocks){ + const string identCondition=expression.bindings.front(); + for(auto scope : expression.blocks) { //set binding resolution - ScopedSymbol symbolInternal = scope->getSymbol(identCondition); + ScopedSymbol symbolInternal=scope->getSymbol(identCondition); getSymbolCache().setCachedValue(Symbol{symbolInternal, scope}, InterpretationResolution(resolutionCondition)); - resolution = unify(resolution, Parent::process(scope, context)); + resolution=unify(resolution, Parent::process(scope, context)); } - for(auto scope: expression.blocks){ - resolution = unify(resolution, Parent::process(scope, context)); + for(auto scope : expression.blocks) { + resolution=unify(resolution, Parent::process(scope, context)); } break; } case Operator::LIST: - case Operator::LIST_NAMED: { - for (const Expression &op: expression.getOperands()) { - resolution = unify(resolution, process(op, context)); + case Operator::LIST_NAMED: + { + for(const Expression &op : expression.getOperands()) { + resolution=unify(resolution, process(op, context)); } break; } - case Operator::VARIANT: { + case Operator::VARIANT: + { if(expression.getOperands().size()){ - resolution = process(expression.getOperands().front(), context); + resolution=process(expression.getOperands().front(), context); } else { - resolution = ANY; + resolution=ANY; } break; } - default: { - resolution = CMPL_ONLY; + default: + { + resolution=CMPL_ONLY; - for (const Expression &op: expression.getOperands()) { + for(const Expression &op : expression.getOperands()) { process(op, context); } - for (CodeScope* scope: expression.blocks) { + for(CodeScope* scope : expression.blocks) { Parent::process(scope, context); } break; } - } + } - InterpretationResolution resolutionExpected = - Attachments::get(expression, {ANY, NONE}).resolution; + InterpretationResolution resolutionExpected= + Attachments::get(expression,{ANY, NONE}).resolution; - resolution = unify(resolution, resolutionExpected); - if (resolution != resolutionExpected && (op!=NONE || resolution == INTR_ONLY)){ - Attachments::put(expression, {resolution, op}); + resolution=unify(resolution, resolutionExpected); + if(resolution!=resolutionExpected&&(op!=NONE||resolution==INTR_ONLY)){ + Attachments::put(expression,{resolution, op}); } return resolution; } - InterpretationResolution - InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context){ - return process(function); - } +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(); +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); + if(cache.isCached(symbSelfFunc)) + return cache.getCachedValue(symbSelfFunc); - const FunctionInterpretationData& fnSignature = FunctionInterpretationHelper::getSignature(function); - InterpretationResolution fnResolutionExpected = details::recognizeTags(function->getTags()); + 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)); + //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< size; ++argNo){ - Symbol symbArg{ScopedSymbol{entry->__identifiers.at(arguments[argNo]), versions::VERSION_NONE}, entry}; - cache.setCachedValue(symbArg, InterpretationResolution(fnSignature.signature[argNo])); - } + } else { + // - in order to recognize indirect recursion mark this function resolution as POSTPONED + cache.setCachedValue(symbSelfFunc, FUNC_POSTPONED); + } - PassContext context; - context.function = function; - context.scope = entry; - InterpretationResolution resActual = process(CodeScope::getDeclaration(symbSelfFunc), context); - resActual = unify(resActual, fnResolutionExpected); - return cache.setCachedValue(symbSelfFunc, move(resActual)); + //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])); } - const FunctionInterpretationData - FunctionInterpretationHelper::getSignature(ManagedFnPtr function){ - if (Attachments::exists(function)){ - return Attachments::get(function); - } + 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)); +} - FunctionInterpretationData&& data = recognizeSignature(function); - Attachments::put(function, data); - return data; +const FunctionInterpretationData +FunctionInterpretationHelper::getSignature(ManagedFnPtr function) { + if(Attachments::exists(function)){ + return Attachments::get(function); } - FunctionInterpretationData - FunctionInterpretationHelper::recognizeSignature(ManagedFnPtr function){ - CodeScope* entry = function->__entry; - FunctionInterpretationData result; - result.signature.reserve(entry->__bindings.size()); + 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 < size; ++no){ - const std::string& argName = entry->__bindings[no]; - Symbol symbArg{ScopedSymbol{entry->__identifiers.at(argName), versions::VERSION_NONE}, entry}; + 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::getDeclaration(symbArg); + const Expression& arg=CodeScope::getDefinition(symbArg); - InterpretationResolution argResolution = details::recognizeTags(arg.tags); - flagPartialInterpretation |= (argResolution == INTR_ONLY); + InterpretationResolution argResolution=details::recognizeTags(arg.tags); + flagPartialInterpretation|=(argResolution==INTR_ONLY); - result.signature.push_back(argResolution); - } - result.flagPartialInterpretation = flagPartialInterpretation; - return result; + 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 +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) + * + * Recognizes program functions, expressions, instructions eligible for interpretation + * and stores output in Attachments and Attachments + * + * There are number of instructions currently able to be interpreted: + * - 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 *hybrid 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) + */ \ No newline at end of file diff --git a/cpp/src/pass/interpretationpass.h b/cpp/src/pass/interpretationpass.h index 1e2a54a..b5ebf20 100644 --- a/cpp/src/pass/interpretationpass.h +++ b/cpp/src/pass/interpretationpass.h @@ -1,91 +1,92 @@ /* 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. namespace xreate{ namespace interpretation{ enum InterpretationResolution{ANY, INTR_ONLY, CMPL_ONLY, FUNC_POSTPONED}; enum InterpretationOperator{NONE, IF_INTERPRET_CONDITION, FOLD_INTERPRET_INPUT, SWITCH_INTERPRET_CONDITION, SWITCH_VARIANT, 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. */ 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/versionspass.cpp b/cpp/src/pass/versionspass.cpp index 328922d..7b2acf4 100644 --- a/cpp/src/pass/versionspass.cpp +++ b/cpp/src/pass/versionspass.cpp @@ -1,369 +1,375 @@ /* 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 */ -#include +/** \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{ namespace versions{ template<> std::list defaultValue>(){ return std::list(); }; 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::getDeclaration(symbol); + 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 diff --git a/cpp/src/pass/versionspass.h b/cpp/src/pass/versionspass.h index 6397451..2ce01b6 100644 --- a/cpp/src/pass/versionspass.h +++ b/cpp/src/pass/versionspass.h @@ -1,110 +1,119 @@ /* 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 + */ + #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 + * \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 */ 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 a056a88..fe51b5f 100644 --- a/cpp/src/query/containers.cpp +++ b/cpp/src/query/containers.cpp @@ -1,173 +1,178 @@ /* 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(ClaspLayer* clasp) { if (flagIsDataLoaded) return; map prototypes; map roots; //read all proto data auto range = clasp->query(Config::get("containers.id.prototypes")); if (range) for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom) { auto data = ClaspLayer::parse(atom->second); Symbol root = clasp->unpack(get<0> (data)); Symbol prototype = clasp->unpack(get<1> (data)); prototypes[root] = prototype; } // fill implementation data for a data sources: range = clasp->query(Config::get("containers.id.implementations")); if (range) for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom) { auto data = ClaspLayer::parse(atom->second); Symbol var = clasp->unpack(get<0>(data)); string implSerialized = get<1>(data); //data source, has no prototypes: if (!prototypes.count(var)) { Implementation impl = Implementation::create(var); Attachments::put(var, move(impl)); continue; } roots.emplace(move(var), move(implSerialized)); } //fill implementation data for a cluster roots for (const pair & root: roots) { Symbol prototype = prototypes[root.first]; while (prototypes.count(prototype)) { prototype = prototypes.at(prototype); } Attachments::put(root.first, Implementation(Attachments::get(prototype))); } // read cluster data and fill implementation data for cluster members range = clasp->query(Config::get("containers.id.clusters")); if (range) for(ClaspLayer::ModelIterator atom = range->first; atom != range->second; ++atom) { auto info = ClaspLayer::parse(atom->second); Symbol root = clasp->unpack(get<0>(info)); Symbol child = clasp->unpack(get<1>(info)); if (!(child == root) && (Attachments::exists(root))) { Implementation rootImpl = Attachments::get(root); Attachments::put(child, move(rootImpl)); } } flagIsDataLoaded = true; } //static ImplementationData* create(Symbol var, std::string implSerialized, const ImplementationData* implPrototype); Implementation Implementation::create(const Symbol &var) { //TODO review implementation determination strategy - Expression varDecl = CodeScope::getDeclaration(var); + 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(); } Implementation Implementation::create(const Symbol& var, const std::string& implSerialized) { - Expression varDecl = CodeScope::getDeclaration(var); + Expression varDecl = CodeScope::getDefinition(var); if (implSerialized == Config::get("containers.impl.solid")) { return {SOLID, ImplementationRec{varDecl.operands.size()}}; } else if (implSerialized == Config::get("containers.impl.onthefly")) { return {ON_THE_FLY, ImplementationRec{var}}; } 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::getDeclaration(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 456093b..16873b4 100644 --- a/cpp/src/query/containers.h +++ b/cpp/src/query/containers.h @@ -1,90 +1,93 @@ /* 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. */ #ifndef _XREATE_CONTAINERSQUERY_H_ #define _XREATE_CONTAINERSQUERY_H_ #include "xreatemanager.h" #include "clasplayer.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); static Implementation create(const Symbol& var, const Implementation& proto); template const ImplementationRec& extract() const{ const ImplementationRec& rec = boost::get>(data); return rec; } }; + /** \brief Extracts solution about container implementation + * \sa xreate::containers::Iterator + */ class Query : public xreate::IQuery { public: static Implementation queryImplementation(xreate::Symbol const &s); void init(ClaspLayer* clasp); Query(); ~Query(){} private: bool flagIsDataLoaded = 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/context.cpp b/cpp/src/query/context.cpp index 2e8093b..ba9acdd 100644 --- a/cpp/src/query/context.cpp +++ b/cpp/src/query/context.cpp @@ -1,210 +1,219 @@ /* 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/. - * + * * context.cpp * * Created on: Dec 1, 2015 * Author: pgess */ +/** + * \file query/context.h + * \brief Represents reasoner's solution on [Context](/w/concepts/context) + * + * \class xreate::context::ContextQuery + * Currently context compilation governed by xreate::compilation::BasicCodeScopeUnit + * \sa LateContextCompiler2, [Context Concept](/w/concepts/context) + */ + #include #include #include using namespace std; namespace xreate { namespace context { const Domain domainEmpty; const Decisions decisionsEmpty; const FunctionDemand functionDemandEmpty; ContextQuery::ContextQuery(){} const Domain& ContextQuery::getContext(const ScopePacked& scopeId) const{ if (!__modelContext.count(scopeId)){ return domainEmpty; } return __modelContext.at(scopeId); } const Domain& ContextQuery::getContext(CodeScope* const scope) const{ return getContext(clasp->pack(scope)); } //DEBT compatibility of forced context with context resolution for interpretation void ContextQuery::forceContext(const ScopePacked& scopeId, std::list context){ // TODO remove forced context of the same class/type only, possibly //remove any previous forced context for this scopeId //__modelForcedContext.erase(scopeId); //TASK restore forceContext //std::multimap __modelForcedContext; /* std::transform(context.begin(), context.end(), inserter(__modelForcedContext, __modelForcedContext.end()), [scopeId](const Expression& context){return make_pair(scopeId, context);}); */ } void ContextQuery::init(ClaspLayer* clasp){ const std::string& atomBinding = Config::get("clasp.bindings.scope"); this->clasp = clasp; ClaspLayer::ModelFragment query = clasp->query(atomBinding); //static context if (query){ map> dictContext; for (auto i = query->first; i!=query->second; ++i){ ScopePacked idScope; Expression context; std::string link; tie(idScope, context, link) = ClaspLayer::parse(i->second); if (link == "strong") { dictContext[idScope].push_back(context); } } for (map>::value_type& entry: dictContext){ __modelContext.insert(move(entry)); } } prepareFunctionDemandModel(); prepareDecisionModels(); } void ContextQuery::prepareFunctionDemandModel(){ const std::string& atomFunctionDemand = Config::get("clasp.bindings.function_demand"); ClaspLayer::ModelFragment query = clasp->query(atomFunctionDemand); if (query) for (auto i = query->first; i!=query->second; ++i){ string function; Expression topic; tie(function, topic) = ClaspLayer::parse(i->second); FunctionDemand& demand = __modelFunctionDemand[function]; demand.left.insert(make_pair(demand.left.size(), topic)); } } void ContextQuery::prepareDecisionModels(){ const std::string& atomDecision = Config::get("clasp.bindings.scope_decision"); const std::string& atomDependentDecision = Config::get("clasp.context.decisions.dependent"); std::multimap modelDomains; ClaspLayer::ModelFragment query = clasp->query(atomDecision); if (query){ for (auto i = query->first; i!=query->second; ++i){ ScopePacked scopeId; Expression topic; Expression decision; std::tie(scopeId, topic, decision) = ClaspLayer::parse(i->second); if (decision.getValueString() == atomDependentDecision) { assert(decision.operands.size() == 2); const Expression& decisionGuard = decision.operands[1]; const Expression& decisionValue = decision.operands[0]; __modelDependentDecisions[scopeId][topic].emplace(decisionGuard, decisionValue); modelDomains.emplace(topic, decisionValue); } else { Decisions& decisionsOfScope = __modelStaticDecisions[scopeId]; assert(decisionsOfScope.emplace(topic, decision).second && "Possibly more than one decision"); modelDomains.emplace(topic, decision); } } } //populate topic domains: auto adapter = [](const std::pair& p){ return p.second; }; auto iBegin = modelDomains.begin(); while (iBegin!=modelDomains.end()){ const Expression topic = iBegin->first; auto iEnd = modelDomains.upper_bound(topic); auto iBeginAdapted = boost::make_transform_iterator(iBegin,adapter); auto iEndAdapted = boost::make_transform_iterator(iEnd,adapter); Domain dom(iBeginAdapted, iEndAdapted); __modelTopicDomains.emplace(topic, move(dom)); iBegin = iEnd; } } const FunctionDemand& ContextQuery::getFunctionDemand(const std::string& name) const { if (__modelFunctionDemand.count(name)){ return __modelFunctionDemand.at(name); } return functionDemandEmpty; } const Decisions& ContextQuery::getFinalDecisions(const ScopePacked& scopeId) const{ if (__modelStaticDecisions.count(scopeId)){ return __modelStaticDecisions.at(scopeId); } return decisionsEmpty; } const Domain& ContextQuery::getTopicDomain(const Expression& topic) const{ if (__modelTopicDomains.count(topic)){ return __modelTopicDomains.at(topic); } return domainEmpty; } const DependentDecision& ContextQuery::getDependentDecision(ScopePacked scope, const Expression& topic) const{ auto itDecisionsAllTopics = __modelDependentDecisions.find(scope); if (itDecisionsAllTopics != __modelDependentDecisions.end()){ auto itDecisions = itDecisionsAllTopics->second.find(topic); if (itDecisions != itDecisionsAllTopics->second.end()){ return itDecisions->second; } } return decisionsEmpty; } // const std::string& atomLateBinding = Config::get("clasp.bindings.function_uncertain"); // query = clasp->query(atomLateBinding); // // std::map> dictFunctionDomain; // if (query){ // for (auto i = query->first; i!=query->second; ++i){ // string nameFunction; // Expression context; // tie(nameFunction, context) = ClaspLayer::parse(i->second); // dictFunctionDomain.at(nameFunction).push_back(context); // } // // for(auto& entry: dictFunctionDomain){ // __modelFunctionDomain.emplace(entry.first, move(entry.second)); // } // } }} /* namespace xreate::context */ diff --git a/cpp/src/query/context.h b/cpp/src/query/context.h index e2f241b..93f99cf 100644 --- a/cpp/src/query/context.h +++ b/cpp/src/query/context.h @@ -1,98 +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/. * * context.h * * Created on: Dec 1, 2015 * Author: pgess */ #ifndef SRC_QUERY_CONTEXT_H_ #define SRC_QUERY_CONTEXT_H_ #include "clasplayer.h" #include "ast.h" #include "serialization.h" #include #include #include #include #include #include namespace xreate{ namespace context{ typedef ExpressionSerialization::Serializer Domain; typedef boost::bimap FunctionDemand; typedef std::map Decisions; typedef std::map DependentDecision; +/** \brief Extracts logic solver's solutions about Context */ class ContextQuery: public IQuery { //AdhocQuery(); public: + /**\brief Returns scope's context */ const Domain& getContext(const ScopePacked& scopeId) const; + /**\brief Returns scope's context */ const Domain& getContext(CodeScope* const scope) const; void forceContext(const ScopePacked& scopeId, std::list context); const Domain& getTopicDomain(const Expression& topic) const; const DependentDecision& getDependentDecision(ScopePacked scope, const Expression& topic) const; const FunctionDemand& getFunctionDemand(const std::string& name) const; const Decisions& getFinalDecisions(const ScopePacked& scopeId) const; virtual void init(ClaspLayer* clasp); ContextQuery(); virtual ~ContextQuery(){}; private: ClaspLayer* clasp; std::map __modelContext; std::map __modelFunctionDemand; std::map __modelStaticDecisions; std::map __modelTopicDomains; std::map> __modelDependentDecisions; void prepareFunctionDemandModel(); void prepareDecisionModels(); }; }} // namespace xreate::context */ /* template class ContextAttachments: private std::unordered_map { typedef std::unordered_map PARENT; public: ContextAttachments(ContextAttachments&& other) : PARENT(std::move(other)), domain(std::move(other.domain)) {} ContextAttachments(std::vector&& expressions, std::vector&& attachments) : domain(move(expressions)) { size_t size = domain.size(); for (size_t i=0; i FunctionSpecializations ; */ #endif /* SRC_QUERY_CONTEXT_H_ */ diff --git a/cpp/src/serialization.h b/cpp/src/serialization.h index 49d2d14..c50d932 100644 --- a/cpp/src/serialization.h +++ b/cpp/src/serialization.h @@ -1,33 +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/. * * serialization.h * * Created on: 03/13. 2016 * Author: pgess */ +/** + * \file + * \brief Serialization: mechanism to represent expressions in compact form for efficient processing + */ + #ifndef SRC_SERIALIZATION_SERIALIZATION_H_ #define SRC_SERIALIZATION_SERIALIZATION_H_ #include "aux/serialization/expressionserializer.h" namespace xreate { struct RequirementIntegralCode{}; template struct ExpressionSerialization { typedef PackedExpression Code; typedef ExpressionSerializer Serializer; }; template<> struct ExpressionSerialization{ typedef size_t Code; typedef ExpressionSerializerIntegral Serializer; }; } #endif /* SRC_SERIALIZATION_SERIALIZATION_H_ */ diff --git a/cpp/src/utils.cpp b/cpp/src/utils.cpp index 94fdd66..b138bff 100644 --- a/cpp/src/utils.cpp +++ b/cpp/src/utils.cpp @@ -1,33 +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 + * \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/utils.h b/cpp/src/utils.h index 5b9bc56..9632983 100644 --- a/cpp/src/utils.h +++ b/cpp/src/utils.h @@ -1,168 +1,166 @@ /* 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 */ #ifndef UTILS_H #define UTILS_H #include "jeayeson/jeayeson.hpp" -//TODO use type mark to mark dirty/mutable members +namespace xreate { + +template +struct AddTag { -/* -template -struct DdesctructableClass { + explicit + AddTag(const Source &src) + : __src(src) { } -} -*/ -/* -template -struct TagUpdatable{ - TagUpdatable(const OriginalType& source) - : __source(source) - {} + explicit + AddTag(Source &&src) + : __src(std::move(src)) { } - TagUpdatable() = delete; + operator const Source&() const{ + return __src; + } - const OriginalType& __source; + const Source& get() const{ + return __src; + } + + const Source* + operator->() const { + return &__src; + } + +private: + Source __src; }; +struct Expand_t{}; +template +using Expanded = AddTag; + -struct Updatable; -template -struct TagsDictionary -{}; +//DEBT move to resources compiler. https://github.com/markusfisch/cpprc +class Config { +private: + json_map __storage; + static Config __self; + Config(); -template -struct TagsDictionary -{ - typedef TagUpdatable TagName; +public: + static std::string get(std::string key) { + return __self.__storage.get_for_path(key).get(); + } }; -template -struct awareOf -{ - awareOf(OT& dest) - : __dest(dest) - {} - - awareOf& - operator= (const typename TagsDictionary::TagName& source) - { - __dest = source.__source; - } +/** \brief Decorators support */ +template +struct DecoratorsDict{ + //typedef ConcreteDecoratorForTag result; +}; -private: - OT& __dest; +template +struct Decorators{ + typedef typename DecoratorsDict::result Instance; + + template + static Instance* getInterface(Base* obj){ + return dynamic_cast< Instance* > (obj); + } }; -template> -const OT& -awareOf(const Tag& holder) -{ - return std::forward(holder.__source); -} +template +struct ManagedPtr { + static ManagedPtr Invalid() { + return ManagedPtr(); + } - */ + ManagedPtr() : __storage(0) { + } -namespace xreate { - template - struct AddTag { + ManagedPtr(unsigned int id, const std::vector* storage) + : __id(id), __storage(storage) { + } - explicit - AddTag(const Source &src) - : __src(src) { } + Target& + operator*() const { + assert(isValid() && "Invalid Ptr"); + return *__storage->at(__id); + } - explicit - AddTag(Source &&src) - : __src(std::move(src)) { } + void operator=(const ManagedPtr& other) { + __id = other.__id; + __storage = other.__storage; + } - operator const Source&() const{ - return __src; - } + bool + operator==(const ManagedPtr& other) { + return isValid() && (__id == other.__id); + } - const Source& get() const{ - return __src; - } + Target* + operator->() const noexcept { + assert(isValid() && "Invalid Ptr"); + return __storage->at(__id); + } - const Source* - operator->() const { - return &__src; - } + inline bool isValid() const { + return (__storage) && (0 <= __id) && (__id < __storage->size()); + } - private: - Source __src; - }; + inline operator bool() const { + return isValid(); + } - struct Expand_t{}; - template - using Expanded = AddTag; - + ManagedPtr& operator++() { + ++__id; + return *this; + } + inline unsigned int id() const { + return __id; + } + +private: + unsigned int __id = 0; + const std::vector * __storage = 0; +}; - //DEBT move to resources compiler. https://github.com/markusfisch/cpprc - class Config { - private: - json_map __storage; - static Config __self; - Config(); - - public: - static std::string get(std::string key) { - return __self.__storage.get_for_path(key).get(); - } - }; - - /** - * Decorators support - */ - template - struct DecoratorsDict{ - //typedef ConcreteDecoratorForTag result; - }; - - template - struct Decorators{ - typedef typename DecoratorsDict::result Instance; - - template - static Instance* getInterface(Base* obj){ - return dynamic_cast< Instance* > (obj); - } - }; } std::wstring utf8_to_wstring(const std::string& str); std::string wstring_to_utf8(const std::wstring& str); #define RST "\x1B[0m" #define KRED "\x1B[31m" #define KGRN "\x1B[32m" #define KYEL "\x1B[33m" #define KBLU "\x1B[34m" #define KMAG "\x1B[35m" #define KCYN "\x1B[36m" #define KWHT "\x1B[37m" #define FRED(x) KRED << x << RST #define FGRN(x) KGRN < * Created on July 3, 2017, 6:03 PM */ #include "xreatemanager.h" #include "clasplayer.h" #include "aux/xreatemanager-decorators.h" #include "llvmlayer.h" #include "main/Parser.h" #include #include namespace xreate { void -PassManager::registerPass(AbstractPassBase* pass, const PassId& id, AbstractPassBase* parent) +PassManager::registerPass(IPass* pass, const PassId& id, IPass* parent) { __passes.emplace(id, pass); __passDependencies.emplace(parent, pass); } -AbstractPassBase* +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}; + std::list passes{nullptr}; while (passes.size()){ - AbstractPassBase* parent = passes.front(); + IPass* parent = passes.front(); auto range = __passDependencies.equal_range(parent); for (auto i=range.first; i!=range.second; ++i){ - AbstractPassBase* pass = i->second; + IPass* pass = i->second; pass->run(); pass->finish(); passes.push_back(pass); } passes.pop_front(); } } void PassManager::prepare(AST* ast){ root = ast; clasp = new ClaspLayer(); clasp->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; } } diff --git a/cpp/src/xreatemanager.h b/cpp/src/xreatemanager.h index 16a1f22..800191e 100644 --- a/cpp/src/xreatemanager.h +++ b/cpp/src/xreatemanager.h @@ -1,132 +1,142 @@ /* 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. + * + */ + #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 AbstractPassBase; +class IPass; class ClaspLayer; class LLVMLayer; class AST; enum class PassId { CFGPass, CompilePass, DFGPass, EnvironmentTestsPass, LoggerPass, AdhocPass, RulesPass, InterpretationPass, VersionsPass }; +/** + * \class PassManager + * \brief Base class to control passes + */ class PassManager{ public: void prepare(AST* ast); - void registerPass(AbstractPassBase* pass, const PassId& id, AbstractPassBase* prerequisite=nullptr); - AbstractPassBase* getPassById(const PassId& id); + void registerPass(IPass* pass, const PassId& id, IPass* prerequisite=nullptr); + IPass* getPassById(const PassId& id); bool isPassRegistered(const PassId& id); void executePasses(); virtual ~PassManager(); ClaspLayer* clasp; LLVMLayer* llvm; AST* root; private: - std::map __passes; - std::multimap __passDependencies; + 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: virtual void* run()=0; static XreateManager* prepare(std::string&& code); 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/cfa.cpp b/cpp/tests/cfa.cpp index e1ed8da..13d9141 100644 --- a/cpp/tests/cfa.cpp +++ b/cpp/tests/cfa.cpp @@ -1,122 +1,122 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * testsCFG.cpp * * Created on: Jul 17, 2015 * Author: pgess */ #include "xreatemanager.h" #include "pass/dfapass.h" #include "pass/cfapass.h" #include "analysis/DominatorsTreeAnalysisProvider.h" #include "gtest/gtest.h" #include #include using namespace xreate; using namespace xreate::cfa; using namespace std; TEST(CFA, testFunctionAnnotationsClasp){ string&& program = "f2 = function::int; annotationF2 {\n" " 0\n" "}\n" "\n" "f1 = function:: int; entry; annotationF1 {\n" " f2() + 10\n" "}"; details::tier1::XreateManager* man = details::tier1::XreateManager::prepare(move(program)); man->analyse(); ClaspLayer::ModelFragment answer = man->clasp->query("annotationF1"); int countNoneValue = 0; if (answer) countNoneValue = std::distance(answer->first, answer->second); EXPECT_EQ(1, countNoneValue); answer = man->clasp->query("annotationF2"); countNoneValue = 0; if (answer) countNoneValue = std::distance(answer->first, answer->second); EXPECT_EQ(1, countNoneValue); } TEST(CFA, testLoopContextExists){ details::tier1::XreateManager* man = details::tier1::XreateManager::prepare ( "interface(cfa){\n" " operator fold:: annotation1.\n" "}\n" "\n" "main = function:: int; entry {\n" " x = [1..10]:: [int].\n" " sum = loop fold (x->el:: int, 0->sum):: int {\n" " el + sum + f1()\n" " }. \n" " sum\n" "}" "case context:: annotation1 {" " f1 = function::int {\n" " x = 0:: int. " " x\n" " }" "}" ); man->analyse(); ClaspLayer::ModelFragment model = man->clasp->query("annotation1"); ScopePacked scopeIdActual = std::get<0>(ClaspLayer::parse(model->first->second)); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); - const Expression& exprSum = scopeEntry->getDeclaration(scopeEntry->getSymbol("sum")); + const Expression& exprSum = scopeEntry->getDefinition(scopeEntry->getSymbol("sum")); CodeScope* scopeExpected = exprSum.blocks.front(); ScopePacked scopeIdExpected = man->clasp->pack(scopeExpected); ASSERT_EQ(scopeIdExpected, scopeIdActual); } TEST(CFA, CFGRoots){ std::string program = R"CODE( main = function::int{a()+ b()} a= function::int {c1() + c2()} b= function::int {c2()} c1=function::int{0} c2=function::int{0} )CODE"; boost::scoped_ptr manager (XreateManager::prepare(move(program))); manager->registerPass(new CFAPass(manager.get()) , PassId::CFGPass); manager->executePasses(); manager->clasp->run(); dominators::DominatorsTreeAnalysisProvider domProvider; domProvider.run(manager->clasp); dominators::DominatorsTreeAnalysisProvider::Dominators expectedFDom= { {0, {0, 9}} ,{1, {1, 4}} ,{2, {7, 8}} ,{3, {2, 3}} ,{4, {5, 6}} }; dominators::DominatorsTreeAnalysisProvider::Dominators expectedPostDom= { {0, {5, 6}} ,{1, {3, 4}} ,{2, {8, 9}} ,{3, {1, 2}} ,{4, {7, 10}} }; ASSERT_EQ(expectedFDom, domProvider.getForwardDominators()); ASSERT_EQ(expectedPostDom, domProvider.getPostDominators()); } diff --git a/cpp/tests/externc.cpp b/cpp/tests/externc.cpp index b619517..3686be8 100644 --- a/cpp/tests/externc.cpp +++ b/cpp/tests/externc.cpp @@ -1,115 +1,115 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ - * + * * externc.cpp * * Created on: - * Author: pgess */ #include "gtest/gtest.h" #include "xreatemanager.h" #include "main/Scanner.h" #include "main/Parser.h" #include #include using namespace std; using namespace xreate; TEST(InterfaceExternC, testAST) { std::string code = " \ interface(extern-c){ \ xml2 = library:: pkgconfig(\"libxml-2.0\"). \ \ include { \ xml2 = [\"libxml/tree.h\"] \ }. \ } \ "; xreate::grammar::main::Scanner scanner(reinterpret_cast (code.c_str()), code.size()); xreate::grammar::main::Parser parser(&scanner); parser.Parse(); ASSERT_EQ(1, parser.root->__externdata.size()); for (const ExternEntry& lib : parser.root->__externdata) { ASSERT_EQ("libxml-2.0", lib.package); ASSERT_EQ(1, lib.headers.size()); ASSERT_EQ("libxml/tree.h", lib.headers.at(0)); } } TEST(InterfaceExternC, testfetchPackageHeaders) { ExternEntry entry{"libxml-2.0", {}}; vector args = ExternLayer::fetchPackageFlags(entry); ASSERT_EQ(1, args.size()); ASSERT_EQ("-I/usr/include/libxml2", args.at(0)); } TEST(InterfaceExternC, testfetchPackageLibs) { ExternEntry entry{"libxml-2.0", {}}; vector args = ExternLayer::fetchPackageLibs(entry); ASSERT_EQ(1, args.size()); ASSERT_EQ("xml2", args.at(0)); } TEST(InterfaceExternC, testLoadLib) { std::string msgErr; if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently("-lpcre -lxml2", &msgErr)) { cout << msgErr; ASSERT_EQ("", msgErr); } ASSERT_TRUE(true); } TEST(InterfaceExternC, testBSD1) { std::string code = " \n\ interface(extern-c){ \n\ libbsd = library:: pkgconfig(\"libbsd\"). \n\ \n\ include { \n\ libbsd = [\"bsd/stdlib.h\"] \n\ }. \n\ } \n" "main= function:: int; entry{arc4random_uniform(24) }"; std::unique_ptr program(XreateManager::prepare(move(code))); void* entryPtr = program->run(); int (*entry)() = (int (*)())(intptr_t) entryPtr; int answer = 24; answer = entry(); cout << answer; ASSERT_LT(answer, 24); } TEST(InterfaceExternC, testStructFields1) { FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r"); assert(input != nullptr); xreate::grammar::main::Scanner scanner(input); xreate::grammar::main::Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); - const ExpandedType& t2Tree = ast->getType(body->getDeclaration(body->getSymbol("tree"))); + const ExpandedType& t2Tree = ast->getType(body->getDefinition(body->getSymbol("tree"))); LLVMLayer llvm(ast); TypeUtils utils(&llvm); std::vectorfields = utils.getStructFields(t2Tree); auto field = std::find(fields.begin(), fields.end(), "children"); ASSERT_TRUE(field != fields.end()); } diff --git a/cpp/tests/types.cpp b/cpp/tests/types.cpp index a7493d3..f3de4a3 100644 --- a/cpp/tests/types.cpp +++ b/cpp/tests/types.cpp @@ -1,181 +1,181 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * types.cpp * * Created on: Jun 4, 2015 * Author: pgess */ #include "gtest/gtest.h" #include "xreatemanager.h" #include "llvmlayer.h" #include "main/Parser.h" using namespace std; using namespace xreate; using namespace xreate::grammar::main; TEST(Types, DependantTypes1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string], \n" " content:: string\n" "}.\n"; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeXmlNode = program->root->findType("XmlNode"); ASSERT_EQ(TypeOperator::STRUCT, typeXmlNode->__operator); ASSERT_EQ(3, typeXmlNode->__operands.size()); ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(0).__value); ASSERT_EQ(TypeOperator::ARRAY, typeXmlNode->__operands.at(1).__operator); ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(2).__value); } TEST(Types, ast_ParameterizedTypes_FeatureTypeIndex_1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string],\n" " content:: string\n" "}.\n" "" "Template = type(Leaf) {Leaf, [Leaf[content]]}." "Concrete = type Template(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operator); ASSERT_EQ(2, typeConcrete->__operands.size()); ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operands.at(0).__operator); ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator); ASSERT_EQ(TypePrimitive::String, typeConcrete->__operands.at(1).__operands.at(0).__value); } TEST(Types, TreeType1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string],\n" " content:: string\n" "}.\n" "" "Tree = type(Leaf) {Leaf, [Tree(Leaf)]}." "Concrete = type Tree(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operator); ASSERT_EQ(2, typeConcrete->__operands.size()); ASSERT_EQ(TypeOperator::STRUCT, typeConcrete->__operands.at(0).__operator); ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator); auto typeLink = typeConcrete->__operands.at(1).__operands.at(0); ASSERT_EQ(TypeOperator::LINK, typeLink.__operator); ASSERT_EQ(typeConcrete->conjuctionId,typeLink.conjuctionId); } TEST(Types, TreeType1LLvm){ string&& code = "XmlNode = type {\n" " tag:: string,\n" " /* attrs:: [string],*/\n" " content:: string\n" "}.\n" "" "Tree = type(Leaf) {Leaf, [Tree(Leaf)]}." "Concrete = type Tree(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); llvm::Type* raw = program->llvm->toLLVMType(typeConcrete); } TEST(Types, ArrayOfExternal1){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); - const ExpandedType& t2 = ast->getType(body->getDeclaration(body->getSymbol("childrenRaw"))); + const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("childrenRaw"))); EXPECT_EQ(t2->__operator, TypeOperator::ARRAY); } TEST(Types, ExternType1){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); - const ExpandedType& t2 = ast->getType(body->getDeclaration(body->getSymbol("tree"))); + const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("tree"))); EXPECT_EQ(t2->__operator, TypeOperator::CUSTOM); } TEST(Types, ast_VariantType1){ string&& code = " colors = type variant {RED, BLUE, GREEN}.\n" " test = function:: colors; entry {GREEN()}"; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typ = program->root->findType("colors"); EXPECT_EQ(TypeOperator::VARIANT, typ->__operator); Expression eRed = program->root->findFunction("test")->getEntryScope()->getBody(); EXPECT_EQ(Operator::VARIANT, eRed.op); const ExpandedType& typ2 = program->root->getType(eRed); EXPECT_EQ(TypeOperator::VARIANT, typ2->__operator); } TEST(Types, full_VariantType_Switch1){ string&& code = "colors = type variant{RED, BLUE, GREEN}. \n" " test = function:: colors {GREEN()} \n" "main = function:: int; entry { \n" " switch(test()):: int \n" " case (GREEN()) {0} \n" " case default {1} \n" "}"; XreateManager* man = XreateManager::prepare(move(code)); int (*main)() = (int (*)()) man->run(); EXPECT_EQ(0, main()); } TEST(Types, ast_VariantType2){ std::string script= R"Code( Annotation = type variant { Num:: int, String:: string, Func:: {name::string, arguments::[Expression]} }. )Code"; std::unique_ptr program(XreateManager::prepare(move(script))); ExpandedType typ = program->root->findType("Annotation"); ASSERT_EQ(3, typ.get().fields.size()); } //TEST(Types, A) //TOTEST string type diff --git a/documentation-api/XreateDoxyfile b/documentation-api/XreateDoxyfile new file mode 100644 index 0000000..b6264fb --- /dev/null +++ b/documentation-api/XreateDoxyfile @@ -0,0 +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" + +# 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" + +# 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 + +# 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 +# , /