diff --git a/config/default.json b/config/default.json index 6cc02b4..90aa4c0 100644 --- a/config/default.json +++ b/config/default.json @@ -1,74 +1,74 @@ { "containers": { "id": { "implementations": "containers_impl", "linkedlist": "linkedlist" }, "impl": { "solid": "solid", "onthefly": "on_the_fly" } }, "logging": { "id": "logging" }, "function-entry": "entry", "transcend": { "bindings" : { "variable": "bind", "function": "bind_func", "scope": "bind_scope", "function_demand" : "bind_function_demand", "scope_decision": "bind_scope_decision" }, "context" : { "decisions":{ "dependent": "resolution_dependency" } }, "nonevalue": "nonevalue", "ret": { "symbol": "retv", "tag": "ret" } }, "tests": { "template": "documentation", "templates": { - "troubleshooting":"Loop.Doc_LoopLoopMap", - "documentation":"Modules.Doc_*:Interpretation.Doc_*:AST.Doc_*:Loop.Doc_*", + "troubleshooting":"Transcend.Doc_*", + "documentation":"Modules.Doc_*:Interpretation.Doc_*:AST.Doc_*:Loop.Doc_*:LateReasoning.Doc_*:Latex.Doc_*:Polymorphs.Doc_*:Transcend.Doc_*", "default": "*", "ast": "AST.*", "effects": "Effects.*", "basic": "Attachments.*", "compilation": "Compilation.*", "communication": "Communication.*", "cfa": "CFA.*", "containers": "Containers.*", "dfa": "DFA.*", "diagnostic": "Diagnostic.*", "dsl": "Association.*:Interpretation.*", "exploitation": "Exploitation.*", "ExpressionSerializer": "ExpressionSerializer.*", "externc": "InterfaceExternC.*", "loops": "Loop.*", "latereasoning": "LateReasoning.*", "latex": "Latex.*", "modules": "Modules.*", "polymorphs": "Polymorphs.*", "intrinsic-query": "Types.SlaveTypes*:Association.TypedQuery*", "types": "Types.*", "virtualization": "Virtualization.*", "vendorsAPI/clang": "ClangAPI.*", - "vendorsAPI/xml2": "libxml2*" + "vendorsAPI/xml2": "libxml2.*" } } } diff --git a/cpp/src/analysis/DominatorsAnalysisProvider.cpp b/cpp/src/analysis/DominatorsAnalysisProvider.cpp index 69f4b71..57b24f0 100644 --- a/cpp/src/analysis/DominatorsAnalysisProvider.cpp +++ b/cpp/src/analysis/DominatorsAnalysisProvider.cpp @@ -1,274 +1,284 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: DominatorsAnalysisProvider.cpp * Author: pgess * * Created on May 13, 2016, 11:39 AM */ /** * \file DominatorsAnalysisProvider.h * \brief Dominators Tree analysis */ #include "analysis/cfagraph.h" #include "analysis/DominatorsAnalysisProvider.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 CFAGraphAdapter; struct ScopeNode { ScopePacked id; CFAGraphAdapter* parent; std::list nodesFrom; std::list nodesTo; CFAGraphAdapter* getParent(){ return parent; } void printAsOperand(llvm::raw_ostream&, bool) {} ScopeNode(ScopePacked scope, CFAGraphAdapter* parentAdapter): id(scope), parent(parentAdapter) {} }; } } //end of namespace xreate::dominators namespace llvm { using namespace xreate::dominators; template<> struct GraphTraits { typedef ScopeNode* NodeRef; typedef std::list::iterator ChildIteratorType; static ChildIteratorType child_begin(ScopeNode* node) { return node->nodesTo.begin(); } static ChildIteratorType child_end(ScopeNode* node) { return node->nodesTo.end(); } }; template<> struct GraphTraits> { typedef ScopeNode* NodeRef; typedef std::list::iterator ChildIteratorType; static ChildIteratorType child_begin(ScopeNode* node) { return node->nodesFrom.begin(); } static ChildIteratorType child_end(ScopeNode* node) { return node->nodesFrom.end(); } }; template<> struct GraphTraits { typedef std::list::iterator nodes_iterator; static nodes_iterator nodes_begin(CFAGraphAdapter* graph) { return graph->nodes.begin(); } static nodes_iterator nodes_end(CFAGraphAdapter* graph) { return graph->nodes.end(); } static ScopeNode* getEntryNode(CFAGraphAdapter* F) { return F->nodeRoot; } static unsigned int size(CFAGraphAdapter* graph) { return graph->nodes.size(); } + + static nodes_iterator + child_begin(ScopeNode* node) { + return node->nodesTo.begin(); + } + + static nodes_iterator + child_end(ScopeNode* node) { + return node->nodesTo.end(); + } }; } namespace xreate { namespace dominators { class DominatorTree : public llvm::DominatorTreeBase { friend class DominatorsAnalysisProvider; public: DominatorTree() : llvm::DominatorTreeBase() {} void run(CFAGraphAdapter& program) { recalculate(program); updateDFSNumbers(); //extract dominators info for(auto& entry : DomTreeNodes) { if(!entry.getFirst()) continue; dominators.emplace(entry.getFirst()->id, 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< { friend class DominatorsAnalysisProvider; public: PostDominatorTree() : llvm::DominatorTreeBase() {} void run(CFAGraphAdapter& program) { recalculate(program); updateDFSNumbers(); //extract dominators info for(auto& entry : DomTreeNodes) { if(!entry.getFirst()) continue; dominators.emplace(entry.getFirst()->id, 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<id < b->id;}; auto posLowerBound = std::lower_bound(nodes.begin(), nodes.end(), &elemNew, fnComp); if(posLowerBound==nodes.end()|| (*posLowerBound)->id > scope){ ScopeNode* elemNewP = new ScopeNode(elemNew); *elemNewP = elemNew; return *nodes.insert(posLowerBound, elemNewP); } return *posLowerBound; } CFAGraphAdapter* CFAGraphAdapter::build(const cfa::CFAGraph* graph) { return build(graph->__dependencyRelations); } CFAGraphAdapter* CFAGraphAdapter::build(const std::multimap& dataScopesOrder){ CFAGraphAdapter* tree=new CFAGraphAdapter(); enum NODE_MARK{NO_ROOT, POSSIBLE_ROOT}; std::unordered_map nodeMarks; for (const auto& edge: dataScopesOrder){ ScopeNode* nodeTo = tree->registerScope(edge.first); ScopeNode* nodeFrom = tree->registerScope(edge.second); nodeTo->nodesFrom.push_back(nodeFrom); nodeFrom->nodesTo.push_back(nodeTo); nodeMarks.emplace(edge.second, POSSIBLE_ROOT); //weak optional insert auto result = nodeMarks.emplace(edge.first, NO_ROOT); //strong insert or update if(!result.second){ result.first->second = NO_ROOT; } } std::list nodeRoots; for(auto nodeMark: nodeMarks){ if(nodeMark.second == POSSIBLE_ROOT) nodeRoots.push_back(nodeMark.first); } if(nodeRoots.size()>1){ ScopeNode* nodeGlobalRoot = tree->registerScope(SCOPE_ABSTRACT_GLOBAL); for(auto rootLocal: nodeRoots){ ScopeNode* nodeLocalRoot = tree->registerScope(rootLocal); nodeLocalRoot->nodesFrom.push_back(nodeGlobalRoot); nodeGlobalRoot->nodesTo.push_back(nodeLocalRoot); } tree->nodeRoot = nodeGlobalRoot; } else if (nodeRoots.size()==1){ tree->nodeRoot = tree->registerScope(nodeRoots.front()); } else { ScopeNode* nodeGlobalRoot = tree->registerScope(SCOPE_ABSTRACT_GLOBAL); tree->nodeRoot = nodeGlobalRoot; } return tree; } void DominatorsAnalysisProvider::run(CFAGraphAdapter* program) { treeForwardDominators.reset(new DominatorTree()); treePostDominators.reset(new PostDominatorTree()); treeForwardDominators->run(*program); treePostDominators->run(*program); } void DominatorsAnalysisProvider::print(std::ostringstream& output) const { treeForwardDominators->print(output, "cfa_forwdom"); treePostDominators->print(output, "cfa_postdom"); } const DominatorsAnalysisProvider::Dominators& DominatorsAnalysisProvider::getForwardDominators() const { return treeForwardDominators->dominators; } const DominatorsAnalysisProvider::Dominators& DominatorsAnalysisProvider::getPostDominators() const { return treePostDominators->dominators; } DominatorsAnalysisProvider::DominatorsAnalysisProvider() {} DominatorsAnalysisProvider::~DominatorsAnalysisProvider() { } }} //end of namespace xreate::dominators diff --git a/cpp/src/analysis/dfagraph.cpp b/cpp/src/analysis/dfagraph.cpp index ee076b6..aabc49a 100644 --- a/cpp/src/analysis/dfagraph.cpp +++ b/cpp/src/analysis/dfagraph.cpp @@ -1,244 +1,244 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: DFAGraph.h * Author: pgess * */ /** * \file dfagraph.h * \brief Data Flow Analysis(DFA) graph data * */ #include "analysis/dfagraph.h" #include "analysis/utils.h" using namespace std; using namespace xreate::analysis; namespace xreate { namespace dfa { void DFACallInstance::print(std::ostringstream& output) const{ boost::format formatArgs; boost::format formatInstance("dfa_callfn(%1%, %2%)."); switch (type) { case WEAK: formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%))."); break; case STRONG: formatArgs = boost::format("weak(dfa_callargs(%1%, %2%, %3%)).\ndfa_callargs(%1%, %2%, %3%)."); break; } output << formatInstance % analysis::writeSymbolNode(retActual) % fnName << endl; for(std::pair rec: args) { SymbolNode argFormal(rec.first); output << formatArgs % analysis::writeSymbolNode(retActual) % analysis::writeSymbolNode(argFormal) % analysis::writeSymbolNode(rec.second) << endl; } } void DFAGraph::addDependency(const SymbolNode& node, const SymbolNode& subnode){ __dependencies.emplace(node, subnode); if (boost::get(&node)){ __usedSymbols.insert(node); } if (boost::get(&subnode)){ __usedSymbols.insert(node); } } void DFAGraph::printDependencies(std::ostringstream& output) const{ for(const SymbolNode& root: __roots){ printDependency(output, root, root); } } void DFAGraph::printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const { auto range = __dependencies.equal_range(nodeCurrent); for (auto it = range.first; it != range.second; ++it){ if (boost::get(&it->second)){ if (!__usedSymbols.count(it->second)){ printDependency(output, it->second, nodeDependent); continue; } } boost::format formatDependency("dfa_depends(%1%, %2%)."); output << formatDependency % analysis::writeSymbolNode(nodeDependent) % analysis::writeSymbolNode(it->second) << endl; printDependency(output, it->second, it->second); } } void DFAGraph::printInplaceAnnotation(const SymbolNode& node, const Expression& expression) { // write down in-place expression tags: boost::format formatBind("bind(%1%, %2%)."); __usedSymbols.insert(node); for (const string& tag: xreate::analysis::compile(expression)) { __output << formatBind % analysis::writeSymbolNode(node) % tag << endl; } } void DFAGraph::printLateAnnotation(const SymbolNode& node, const Expression& expression, - const std::list& symbols, + const std::list& symbols, const std::list& domains){ boost::format formatLateAnnotation("late(%1%, (%2%), (%3%), %4%):- %5%."); boost::format formatDom("%1%(%2%)"); std::list exprSerialized = xreate::analysis::compile(expression); assert(exprSerialized.size() == 1); list identSymbols, identNames, domainsSerialised; auto domainI = domains.begin(); for(auto symbol: symbols){ identSymbols.push_back(analysis::writeSymbolNode(symbol.second).str()); identNames.push_back(symbol.first); domainsSerialised.push_back((formatDom % *domainI % symbol.first).str()); ++domainI; } __output << formatLateAnnotation % analysis::writeSymbolNode(node) % boost::algorithm::join(identSymbols, ", ") % boost::algorithm::join(identNames, ", ") % exprSerialized.front() % boost::algorithm::join(domainsSerialised, "; ") << endl; } void DFAGraph::printAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){ __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual); boost::format formatAlias("dfa_alias(%1%, %2%)."); __output << formatAlias % analysis::writeSymbolNode(symbFormal) % analysis::writeSymbolNode(symbActual) << endl; } void DFAGraph::printWeakAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual){ __usedSymbols.insert(symbFormal); __usedSymbols.insert(symbActual); boost::format formatAlias("weak(dfa_alias(%1%, %2%))."); __output << formatAlias % analysis::writeSymbolNode(symbFormal) % analysis::writeSymbolNode(symbActual) << endl; } void DFAGraph::printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet){ boost::format formatRet("dfa_fnret(%1%, %2%)."); __usedSymbols.insert(symbolRet); __output << formatRet % function->getName() % analysis::writeSymbolNode(symbolRet) << endl; __roots.insert(symbolRet); } void DFAGraph::addCallInstance(DFACallInstance&& instance){ __usedSymbols.insert(instance.retActual); for(const auto arg: instance.args){ __usedSymbols.insert(SymbolNode(arg.first)); __usedSymbols.insert(arg.second); } __callInstances.push_back(std::move(instance)); } void DFAGraph::print(std::ostringstream& output) const{ output << endl << "%\t\tStatic analysis: DFA" << endl; //Dependencies printDependencies(output); //Add generated report output << __output.str() << endl; //Call instances for(const DFACallInstance& instance: __callInstances){ instance.print(output); } output << endl; } void DFAGraph::printSymbols(TranscendLayer* transcend){ boost::format formatHint("shint(%1%, \"%2%\")."); for (const SymbolNode& node : __usedSymbols) { __output << "v(" << analysis::writeSymbolNode(node) << "). "; if (const SymbolPacked* symbol = boost::get(&node)){ __output << formatHint % analysis::writeSymbolNode(node) % transcend->getHintForPackedSymbol(*symbol); } __output << endl; } } void DFAGraph::printOperator(Operator op, std::list&& operands, int dataOpListSize){ std::string opStr; switch(op){ case Operator::MAP: opStr = "map"; break; case Operator::FOLD: opStr = "fold"; break; case Operator::LIST: opStr = "list"; break; case Operator::LIST_RANGE: opStr = "list_range"; break; case Operator::INDEX: opStr = "index"; break; default: assert(false); } std::ostringstream bufOperands; for(const SymbolNode& operand: operands){ __usedSymbols.insert(operand); bufOperands << analysis::writeSymbolNode(operand) << ", "; } if(op == Operator::LIST){ bufOperands << dataOpListSize << ", "; } boost::format formatOperator("ast_op_%1%(%2%)."); __output << (formatOperator % opStr % bufOperands.str().substr(0, bufOperands.str().size() - 2)) << endl; } }} //end of namespace xreate::dfa diff --git a/cpp/src/analysis/dfagraph.h b/cpp/src/analysis/dfagraph.h index dfe1859..d8817b0 100644 --- a/cpp/src/analysis/dfagraph.h +++ b/cpp/src/analysis/dfagraph.h @@ -1,66 +1,66 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: dfa.h * Author: pgess * * Created on June 27, 2016, 1:50 PM */ #ifndef DFA_H #define DFA_H #include "transcendlayer.h" #include namespace xreate { namespace latereasoning { - typedef std::pair LateSymbolRecognized; + typedef std::pair LateParameter; }} namespace xreate {namespace dfa { enum DFACallInstanceType { STRONG, WEAK }; class DFACallInstance { public: std::string fnName; std::vector> args; SymbolNode retActual; DFACallInstanceType type; void print(std::ostringstream& output) const; }; /** \brief Holds DFA Analysis report produced by DFAPass */ class DFAGraph : public IAnalysisReport { public: // DFAGraph(TranscendLayer* engine): __transcend(engine){} virtual void print(std::ostringstream& output) const override; void addCallInstance(DFACallInstance && instance); void addDependency(const SymbolNode& node, const SymbolNode& subnodeBlock); void printInplaceAnnotation(const SymbolNode& node, const Expression& expression); void printLateAnnotation(const SymbolNode& node, const Expression& expression, - const std::list& symbols, + const std::list& symbols, const std::list& domains); void printAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual); void printWeakAlias(const SymbolNode& symbFormal, const SymbolNode& symbActual); void printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet); void printDependencies(std::ostringstream& output) const; void printSymbols(TranscendLayer* transcend); void printOperator(Operator, std::list&& operands, int dataOpListSize = 0); private: mutable std::ostringstream __output; std::list __callInstances; std::unordered_multimap __dependencies; std::unordered_set __usedSymbols; std::unordered_set __roots; void printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const; }; }} // end of namespace xreate::dfa #endif /* DFA_H */ diff --git a/cpp/src/pass/latereasoningpass.h b/cpp/src/pass/latereasoningpass.h index 6a9cde0..3d293b1 100644 --- a/cpp/src/pass/latereasoningpass.h +++ b/cpp/src/pass/latereasoningpass.h @@ -1,182 +1,188 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on June 7, 2018, 7:20 PM * * \file latereasoningpass.h * \brief latereasoningpass */ #ifndef LATEREASONINGPASS_H #define LATEREASONINGPASS_H #include "pass/dfapass.h" namespace xreate { namespace latereasoning { class LateReasoningScope{ public: LateReasoningScope(LateReasoningScope* parent): __parent(parent){ } - boost::optional + boost::optional recognizeIdentifier(const std::string& identifier){ //Search identifier in the current scope if(__identifiers.count(identifier)){ return make_pair(identifier, __identifiers.at(identifier)); } //Search in the parent scope if(__parent){ return __parent->recognizeIdentifier(identifier); } return boost::none; } void addIdentifier(std::string idenName, const SymbolPacked& identSymbol){ __identifiers.emplace(idenName, identSymbol); } private: std::map __identifiers; LateReasoningScope *__parent; }; /** * \note Limitation: Produces late annotation with target as a symbol to which annotation is attached */ template class LateReasoningDFAPassDecorator: public Parent{ public: LateReasoningDFAPassDecorator(PassManager* manager): Parent(manager){ } void registerLateScope(CodeScope* scope, LateReasoningScope* scopeLate){ __dictScopes.emplace(scope, scopeLate); } private: LateReasoningScope* liftScope(const CodeScope* scope){ while(scope){ if(__dictScopes.count(scope)) return __dictScopes.at(scope); scope = scope->__parent; } return nullptr; } - std::list - recognizeLateIdentifiers(const Expression& expression, LateReasoningScope* scope){ - std::list result; + std::list + recognizeLateParameters(const Expression& expression, LateReasoningScope* scope){ + std::list result; switch(expression.op){ case Operator::CALL: { for(const auto& op: expression.operands){ - std::list opResult = recognizeLateIdentifiers(op, scope); + std::list opResult = recognizeLateParameters(op, scope); result.insert(result.end(), opResult.begin(), opResult.end()); } if(!expression.operands.size()){ if(auto symbolRecognized = scope->recognizeIdentifier(expression.getValueString())){ result.push_back(*symbolRecognized); } } break; } case Operator::NEG: { assert(expression.operands.size() == 1); const Expression &op = expression.operands.at(0); - std::list opResult = recognizeLateIdentifiers(op, scope); + std::list opResult = recognizeLateParameters(op, scope); result.insert(result.end(), opResult.begin(), opResult.end()); }; case Operator::INVALID: { switch(expression.__state){ case Expression::NUMBER: break; default: assert(true); } break; } default: break; } return result; } protected: virtual SymbolNode process(const Expression& expression, PassContext context, const std::string& varDecl="") override{ if(expression.__state == Expression::COMPOUND && expression.op == Operator::SWITCH_LATE){ //Reserve late scope: LateReasoningScope* scopeLate = new LateReasoningScope(liftScope(context.scope)); CodeScope* scopeBody = expression.blocks.front(); registerLateScope(scopeBody, scopeLate); - //Assign late identifiers for(const std::string& identLate: expression.bindings){ ScopedSymbol identLateS = scopeBody->getSymbol(identLate); SymbolPacked identLateSP = Parent::man->transcend->pack(Symbol{identLateS, scopeBody}); + + scopeLate->addIdentifier(identLate, identLateSP); //Assign late identifiers - scopeLate->addIdentifier(identLate, identLateSP); + //Assign type(if isn't provided by user) + Expression identE = scopeBody->getDefinition(identLateS); + if (!identE.type.isValid()){ + Expression sourceE = expression.getOperands().at(0); + Attachments::put(Symbol{identLateS, scopeBody}, Parent::man->root->getType(sourceE)); + } } } return Parent::process(expression, context, varDecl); } virtual void processAnnotations(const Expression& expression, PassContext context, const SymbolNode& ident){ LateReasoningScope* scopeLate = liftScope(context.scope); - if(!expression.tags.size() || !scopeLate) - return Parent::processAnnotations(expression, context, ident); + if(!expression.tags.size()) {return Parent::processAnnotations(expression, context, ident);} + if(!scopeLate) {return Parent::processAnnotations(expression, context, ident);} for(const std::pair& tag: expression.tags){ - std::list symbols = recognizeLateIdentifiers(tag.second, scopeLate); - if(!symbols.size()){ + std::list argsLate = recognizeLateParameters(tag.second, scopeLate); + if(!argsLate.size()){ //Standard compilation Parent::graph->printInplaceAnnotation(ident, tag.second); } else{ //Late compilation std::list domains; - for(const auto& symbol: symbols){ - Symbol symbolUnpacked = Parent::man->transcend->unpack(symbol.second); + for(const auto& arg: argsLate){ + Symbol symbolUnpacked = Parent::man->transcend->unpack(arg.second); ExpandedType typSymbol = Parent::man->root->getType(CodeScope::getDefinition(symbolUnpacked)); assert(typSymbol->__operator == TypeOperator::SLAVE); domains.push_back(typSymbol->__valueCustom); } - Parent::graph->printLateAnnotation(ident, tag.second, symbols, domains); + Parent::graph->printLateAnnotation(ident, tag.second, argsLate, domains); } } } private: std::unordered_map __dictScopes; }; }} #endif /* LATEREASONINGPASS_H */ diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 2d220d4..ee87e1a 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,56 +1,57 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate-tests) find_package(GTest REQUIRED) INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS}) INCLUDE_DIRECTORIES("/usr/include/libxml2") INCLUDE_DIRECTORIES($) # TESTS #========================= FIND_PACKAGE (LLVM REQUIRED) message("LLVM_LIBRARY_DIRS: " ${LLVM_LIBRARY_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) set (LIBCLASP_PATH ${POTASSCO_PATH}/build/debug) link_directories(${LIBCLASP_PATH}) #aux_source_directory(. TEST_FILES) set(TEST_FILES + supplemental/docutils + latetranscend.cpp cfa.cpp latex.cpp polymorph.cpp transcend.cpp -# latereasoning.cpp virtualization.cpp exploitation.cpp effects-communication.cpp association.cpp main.cpp modules.cpp attachments.cpp ast.cpp dfa.cpp compilation.cpp ExpressionSerializer.cpp externc.cpp types.cpp #vendorsAPI/clangAPI.cpp - #vendorsAPI/xml2.cpp + vendorsAPI/xml2.cpp #vendorsAPI/json.cpp containers.cpp interpretation.cpp loops.cpp #supplemental/versions-algorithm-data_dependency.cpp effects-versions.cpp ) add_executable(${PROJECT_NAME} ${TEST_FILES}) target_link_libraries(${PROJECT_NAME} xreate ${GTEST_LIBRARIES} pthread xml2 gcov) add_custom_target (coverage COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/code-coverage.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cpp/tests/ast.cpp b/cpp/tests/ast.cpp index 65f5c69..ab020ca 100644 --- a/cpp/tests/ast.cpp +++ b/cpp/tests/ast.cpp @@ -1,170 +1,205 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * ast.cpp * * Created on: Jun 11, 2015 * Author: pgess */ -#include "gtest/gtest.h" + +#include "supplemental/docutils.h" #include "xreatemanager.h" #include "main/Parser.h" +#include "gtest/gtest.h" + using namespace std; using namespace xreate; using namespace xreate::grammar::main; -TEST(AST, Containers1){ - FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); - Scanner scanner(input); - Parser parser(&scanner); - parser.Parse(); - assert(!parser.errors->count && "Parser errors"); +TEST(AST, Containers1) { + FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r"); + Scanner scanner(input); + Parser parser(&scanner); + parser.Parse(); + assert(!parser.errors->count && "Parser errors"); - fclose(input); + fclose(input); } TEST(AST, InterfacesDataCFA) { - XreateManager* man = XreateManager::prepare - ("interface(cfa){\n" - " operator map :: annotation1.\n" - "}"); + XreateManager* man = XreateManager::prepare + ("interface(cfa){\n" + " operator map :: annotation1.\n" + "}"); - auto answer = man->root->__interfacesData.equal_range(CFA); - EXPECT_EQ(1, std::distance(answer.first, answer.second)); + auto answer = man->root->__interfacesData.equal_range(CFA); + EXPECT_EQ(1, std::distance(answer.first, answer.second)); - Expression&& scheme = move(answer.first->second); + Expression&& scheme = move(answer.first->second); - EXPECT_EQ(Operator::MAP, scheme.op); - EXPECT_EQ("annotation1", scheme.getOperands().at(0).getValueString()); + EXPECT_EQ(Operator::MAP, scheme.op); + EXPECT_EQ("annotation1", scheme.getOperands().at(0).getValueString()); } -TEST(AST, syntax_recognizeIdentifiers){ - XreateManager* man = XreateManager::prepare(R"Code( +TEST(AST, syntax_recognizeIdentifiers) { + XreateManager* man = XreateManager::prepare(R"Code( test= function(a:: num):: num; entry { a = b:: int. b = 8:: int. a } )Code"); } -TEST(AST, syntax_operatorIndex){ - XreateManager* man = XreateManager::prepare(R"Code( +TEST(AST, syntax_operatorIndex) { + XreateManager* man = XreateManager::prepare(R"Code( test= function(a:: num):: num; entry { b = a[1]. b } )Code"); } -TEST(AST, Variants_switch){ -XreateManager* man = XreateManager::prepare(R"Code( +TEST(AST, Variants_switch) { + XreateManager* man = XreateManager::prepare(R"Code( Color = type variant{Blue, White, Green}. main = function:: int { x = White()::Color. switch variant(x)::int case (Green) {0} case (White) {1} case (Blue){2} } )Code"); - Expression e= man->root->findFunction("main")->getEntryScope()->getBody(); - ASSERT_EQ(4, e.getOperands().size()); - ASSERT_EQ(3, e.blocks.size()); -} - -TEST(AST, DISABLED_InterfacesDataDFA){ - + Expression e = man->root->findFunction("main")->getEntryScope()->getBody(); + ASSERT_EQ(4, e.getOperands().size()); + ASSERT_EQ(3, e.blocks.size()); } -TEST(AST, DISABLED_InterfacesDataExtern){ +TEST(AST, DISABLED_InterfacesDataDFA) { } -} +TEST(AST, DISABLED_InterfacesDataExtern) { } -TEST(AST, Doc_LiteralsAndExpressions){ - XreateManager* man = XreateManager::prepare( +TEST(AST, Doc_LiteralsAndExpressions) { + XreateManager* man = XreateManager::prepare( R"Code( Record1 = type {year:: int, month:: string}. isOdd = function(x :: int) :: bool {true} test = function:: bool; entry { x1 = 5 :: int. x2 = "Nimefurahi kukujua":: string. x3 = {year = 1934, month = "april"}:: Record1. x4 = {16, 8, 3} :: [int]. x5 = 8>=3:: bool. x6 = "Blue" <> "Green" :: bool. colors = {"Green", "Blue"} :: [string]. color = colors[0] :: string. date = {year = 1934, month = "april"}:: Record1. year = date["year"] :: int. a = 0::int. b = 0 :: int. x7 = a - b:: int. result = isOdd(6) :: bool. true } )Code"); - ASSERT_TRUE(true); + ASSERT_TRUE(true); } -TEST(AST, Doc_CodeBlocks){ - XreateManager* man = XreateManager::prepare( - R"Code( - test = function:: int; entry { - a = 10:: int. - b = 2:: int. - - a + b:: int - } +TEST(AST, Doc_CodeBlocks1) { + XreateManager* man = XreateManager::prepare( + getDocumentationExampleById("documentation/Syntax/syntax.xml", "CodeBlocks1")); - )Code"); - - ASSERT_TRUE(true); + ASSERT_TRUE(true); } -TEST(AST, Doc_Functions){ - XreateManager* man = XreateManager::prepare( - R"Code( +TEST(AST, Doc_Functions1) { + XreateManager* man = XreateManager::prepare( + getDocumentationExampleById("documentation/Syntax/syntax.xml", "Functions1")); - sum = function(x:: int, y:: int):: int; entry; status(needs_review) - { - x+y - } + ASSERT_TRUE(true); +} - )Code"); +TEST(AST, Doc_FunctionSpecializations1) { + XreateManager* man = XreateManager::prepare( + getDocumentationExampleById("documentation/Syntax/syntax.xml", "FunctionSpecialization1")); - ASSERT_TRUE(true); + ASSERT_TRUE(true); } -TEST(AST, Doc_BranchStatements){ - XreateManager* man = XreateManager::prepare( +TEST(AST, Doc_BranchStatements) { + string code_IfStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "IfStatement1"); + string code_SwitchStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "SwitchStatement1"); + + string code = R"Code( +test = function:: int; entry +{ + question = "Favorite color?":: string. + monthNum = 2:: int. - test = function:: int; entry - { - question = "Favorite color?":: string. - answer = if (question == "Favorite color?"):: string - {"Yellow"} else {"Don't know"}. + %IfStatement1 + %SwitchStatement1 - monthNum = 2 :: int. - monthName = switch(monthNum) :: string - case (1) {"Jan"} - case (2) {"Feb"} - case default {"Strange.. Don't know this month"}. + monthName +} + )Code"; + replace(code, "%IfStatement1", code_IfStatement1); + replace(code, "%SwitchStatement1", code_SwitchStatement1); - {answer, monthName} - } + XreateManager* man = XreateManager::prepare(move(code)); - )Code"); + ASSERT_TRUE(true); +} + +TEST(AST, Doc_LoopStatements) { + string code_LoopStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement1"); + string code_LoopStatement2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement2"); + string code_FoldStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "FoldStatement1"); + string code_MapStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "MapStatement1"); + + string code = + R"Code( +test = function:: int; entry +{ + %LoopStatement1 + %LoopStatement2 + %FoldStatement1 + %MapStatement1 + + min +} + )Code"; + + replace(code, "%LoopStatement1", code_LoopStatement1); + replace(code, "%LoopStatement2", code_LoopStatement2); + replace(code, "%FoldStatement1", code_FoldStatement1); + replace(code, "%MapStatement1", code_MapStatement1); + replace(code, "COUNTEREXAMPLE", ""); + XreateManager::prepare(move(code)); + + ASSERT_TRUE(true); +} - ASSERT_TRUE(true); +TEST(AST, Doc_Types){ + string code = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Types1"); + XreateManager::prepare(move(code)); + + ASSERT_TRUE(true); } + +TEST(AST, Doc_Variants){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Variants1"); + XreateManager::prepare(move(code_Variants1)); + + ASSERT_TRUE(true); +} \ No newline at end of file diff --git a/cpp/tests/cfa.cpp b/cpp/tests/cfa.cpp index fff0a97..472e186 100644 --- a/cpp/tests/cfa.cpp +++ b/cpp/tests/cfa.cpp @@ -1,248 +1,223 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * cfa.cpp * * Created on: Jul 17, 2015 * Author: pgess */ #include "xreatemanager.h" #include "pass/dfapass.h" #include "pass/cfapass.h" #include "analysis/DominatorsAnalysisProvider.h" #include "analysis/cfagraph.h" #include "pass/compilepass.h" #include "compilation/scopedecorators.h" #include "gtest/gtest.h" #include "aux/xreatemanager-decorators.h" #include #include #include using namespace xreate; using namespace xreate::cfa; using namespace std; TEST(CFA, testFunctionAnnotations) { 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(); StaticModel answer = man->transcend->query("annotationF1"); EXPECT_EQ(1, answer.size()); answer = man->transcend->query("annotationF2"); EXPECT_EQ(1, answer.size()); } 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" "}" "guard:: annotation1 {" " f1 = function::int {\n" " x = 0:: int. " " x\n" " }" "}" ); man->analyse(); StaticModel model = man->transcend->query("annotation1"); ScopePacked scopeIdActual = std::get<0>(TranscendLayer::parse(model.begin()->second)); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); const Expression& exprSum = scopeEntry->getDefinition(scopeEntry->getSymbol("sum")); CodeScope* scopeExpected = exprSum.blocks.front(); ScopePacked scopeIdExpected = man->transcend->pack(scopeExpected); ASSERT_EQ(scopeIdExpected, scopeIdActual); } TEST(CFA, DependenciesFnCall) { details::tier2::XreateManager* man = details::tier2::XreateManager::prepare( R"Code( a = function::int{ seq {x = 0:: int. x} {x = b():: int. x}::int } b = function::int {y = 0. y} )Code"); CodeScope* scopeSeq1 = man->root->findFunction("a")->getEntryScope()->getBody().blocks.front(); CodeScope* scopeSeq2 = *(++man->root->findFunction("a")->getEntryScope()->getBody().blocks.begin()); CodeScope* scopeB = man->root->findFunction("b")->getEntryScope(); ScopePacked psSeq1 = man->transcend->pack(scopeSeq1); ScopePacked psSeq2 = man->transcend->pack(scopeSeq2); ScopePacked psB = man->transcend->pack(scopeB); CFAPass* pass = new CFAPass(man); man->registerPass(pass, PassId::CFAPass); man->executePasses(); const CFAGraph* report = dynamic_cast (man->getPassById(PassId::CFAPass))->getReport(); auto dependencies = report->__dependencyRelations; delete pass; ASSERT_EQ(1, dependencies.count(psSeq2)); ASSERT_EQ(1, dependencies.count(psB)); } TEST(CFA, DependenciesChildScope) { details::tier2::XreateManager* man = details::tier2::XreateManager::prepare( R"Code( a = function::int{ seq {x = 0:: int. x} {x=0::int. if(x>0)::int{1} else {0}}::int } )Code"); CodeScope* scopeSeq1 = man->root->findFunction("a")->getEntryScope()->getBody().blocks.front(); CodeScope* scopeSeq2 = *(++man->root->findFunction("a")->getEntryScope()->getBody().blocks.begin()); CodeScope* scopeIf1 = scopeSeq2->getBody().blocks.front(); CodeScope* scopeIf2 = *(++scopeSeq2->getBody().blocks.begin()); ScopePacked psSeq1 = man->transcend->pack(scopeSeq1); ScopePacked psSeq2 = man->transcend->pack(scopeSeq2); ScopePacked psIf1 = man->transcend->pack(scopeIf1); ScopePacked psIf2 = man->transcend->pack(scopeIf2); CFAPass* pass = new CFAPass(man); man->registerPass(pass, PassId::CFAPass); man->executePasses(); const CFAGraph* report = dynamic_cast (man->getPassById(PassId::CFAPass))->getReport(); auto dependencies = report->__dependencyRelations; delete pass; ASSERT_EQ(0, dependencies.count(psSeq1)); ASSERT_EQ(1, dependencies.count(psSeq2)); ASSERT_EQ(1, dependencies.count(psIf1)); ASSERT_EQ(1, dependencies.count(psIf2)); for(auto rec : dependencies) { std::cout << rec.first << " " << rec.second << std::endl; } } TEST(CFA, Dominators1){ std::multimap dataScopes = { {1, 0}, {2, 0}, {3, 1}, {4, 1}, {4, 2} }; dominators::DominatorsAnalysisProvider domProvider; boost::scoped_ptr adapter(dominators::CFAGraphAdapter::build(dataScopes)); domProvider.run(adapter.get()); dominators::DominatorsAnalysisProvider::Dominators expectedFDom= { {0, {0, 9}} ,{1, {1, 4}} ,{2, {7, 8}} ,{3, {2, 3}} ,{4, {5, 6}} }; dominators::DominatorsAnalysisProvider::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()); } TEST(CFA, Dominators2) { std::string program = R"CODE( a = function:: int; entry{ seq {x = 0:: int. x} {x = 1:: int. x}::int } )CODE"; std::unique_ptr man(details::tier2::XreateManager::prepare(move(program))); CFAPass* pass = new CFAPass(man.get()); man->registerPass(pass, PassId::CFAPass); pass->run(); ScopePacked scope1 = man->transcend->pack(man->root->findFunction("a")->getEntryScope()->getBody().blocks.front()); ScopePacked scope2 = man->transcend->pack(*++man->root->findFunction("a")->getEntryScope()->getBody().blocks.begin()); dominators::DominatorsAnalysisProvider* providerDomAnalysis = new dominators::DominatorsAnalysisProvider(); boost::scoped_ptr adapter(dominators::CFAGraphAdapter::build(pass->getReport())); providerDomAnalysis->run(adapter.get()); dominators::DominatorsAnalysisProvider::Dominators expectedFDom = { {1, {0, 3}}, {2, {1, 2}} }; dominators::DominatorsAnalysisProvider::Dominators expectedPostDom = { {2, {1, 4}}, {1, {2, 3}} }; auto actualFDom = providerDomAnalysis->getForwardDominators(); auto actualPostDom = providerDomAnalysis->getPostDominators(); ASSERT_EQ(expectedFDom, actualFDom); ASSERT_EQ(expectedPostDom, actualPostDom); delete providerDomAnalysis; delete pass; } - -TEST(CFA, ASTCorrespondence_Scope_Bindings_1){ - std::string program = -R"CODE( - test = function(x::int, y::int):: int; entry{ - x + y - } -)CODE"; - - std::unique_ptr man(details::tier2::XreateManager::prepare(move(program))); - CFAPass* pass = new CFAPass(man.get()); - man->registerPass(pass, PassId::CFAPass); - man->executePasses(); - - testing::internal::CaptureStdout(); - man->analyse(); - std::string outputActual = testing::internal::GetCapturedStdout(); - cout << outputActual << endl; - - string outputExpected = "ast_scope_binding(0,0,\"x\")"; - ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); - - outputExpected = "ast_scope_binding(0,1,\"y\")"; - ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); -} diff --git a/cpp/tests/latereasoning.cpp b/cpp/tests/latereasoning.cpp deleted file mode 100644 index fbe8b45..0000000 --- a/cpp/tests/latereasoning.cpp +++ /dev/null @@ -1,184 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - * - * latereasoning.cpp - * - * Author: pgess - * Created on April 21, 2018, 5:10 PM - */ - -#include "xreatemanager.h" -#include "transcendlayer.h" -#include "pass/latereasoningpass.h" -#include "aux/latereasoning.h" -#include "pass/dfapass.h" -#include -#include "gtest/gtest.h" - -using namespace std; -using namespace xreate; -using namespace xreate::latereasoning; - -TEST(LateReasoning, Syntax1) { - XreateManager* man = XreateManager::prepare(R"Code( -test = function:: int { - x = 0::int. - y1= switch late (x)::int{0}. - y2= switch late(x+y1->a::int)::int{1}. - y1+y2 -} -)Code"); - - CodeScope* scope = man->root->findFunction("test")->getEntryScope(); - Expression y1 = scope->getDefinition(scope->getSymbol("y1")); - Expression y2 = scope->getDefinition(scope->getSymbol("y2")); - - ASSERT_EQ(1, y1.bindings.size()); - ASSERT_STRCASEEQ("x", y1.bindings.at(0).c_str()); - - ASSERT_EQ(1, y2.bindings.size()); - ASSERT_STRCASEEQ("a", y2.bindings.at(0).c_str()); -} - -TEST(LateReasoning, Pass_DFAPassDec_1){ - typedef LateReasoningTranscendDecorator LRTranscend; - - auto man = details::tier2::XreateManager::prepare(R"Code( -Dom = type slave dom. - -test = function:: int; entry -{ - LateIdent = 0:: Dom. - 0:: int; ann1(LateIdent) -} -)Code"); - - CodeScope* scopeEntry = man->root->findFunction("test")->getEntryScope(); - ScopedSymbol keyS = scopeEntry->getSymbol("LateIdent"); - SymbolPacked keySP = man->transcend->pack(Symbol{keyS, scopeEntry}); - - std::shared_ptr scopeLateEntry(new LateReasoningScope(nullptr)); - scopeLateEntry->addIdentifier("LateIdent", keySP); - - typedef LateReasoningDFAPassDecorator LRDFAPass; - LRDFAPass* dfaPass = new LRDFAPass(man); - dfaPass->registerLateScope(scopeEntry, scopeLateEntry.get()); - - man->transcend->addRawScript("dom(guard1; guard2).\n"); - - man->registerPass(dfaPass, PassId::DFAPass, nullptr); - man->executePasses(); - - testing::internal::CaptureStdout(); - man->analyse(); - std::string outputActual = testing::internal::GetCapturedStdout(); - cout << outputActual << endl; - - string outputExpected = "late(s(0,-2,0), (s(1,-2,0)), (LateIdent), ann1(LateIdent)):- dom(LateIdent)."; - ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); -} - -TEST(LateReasoning, Transcend_LRTransDec_1) { - Attachments::init(); - typedef LateReasoningTranscendDecorator LRTranscend; - std::unique_ptr transcend(new LRTranscend()); - - std::unique_ptr scope(new CodeScope(nullptr)); - Symbol symbA = scope->addDefinition(Atom("a"), Expression()); - Symbol symbB = scope->addDefinition(Atom("b"), Expression()); - Symbol symbC = scope->addDefinition(Atom("c"), Expression()); - Symbol symbTarget1 = scope->addDefinition(Atom("target1"), Expression()); - Symbol symbTarget2 = scope->addDefinition(Atom("target2"), Expression()); - - SymbolPacked symbpA = transcend->pack(symbA, "a"); - SymbolPacked symbpB = transcend->pack(symbB, "b"); - SymbolPacked symbpC = transcend->pack(symbC, "c"); - SymbolPacked symbTarget1P = transcend->pack(symbTarget1, "target1"); - SymbolPacked symbTarget2P = transcend->pack(symbTarget2, "target2"); - - boost::format formatSymb("s(%1%,%2%,%3%)"); - boost::format formatLateAnnotation1("late(%1%, (%2%, %3%, %4%), (%5%, %6%, %7%), %8%)."); - boost::format formatLateAnnotation2("late(%1%, (%2%, %3%), (%4%, %5%), %6%)."); - #define FORMATSYMBOL(s) (formatSymb % s.identifier % s.version % s.scope).str() - - // Ann1, `variant1` - transcend->addRawScript((formatLateAnnotation1 - % FORMATSYMBOL(symbTarget1P) - % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC) - % "guard1" % "guard1" % "guard1" - % "result(variant1)" - ). str()); - - //Ann1 `result2` variant - transcend->addRawScript((formatLateAnnotation1 - % FORMATSYMBOL(symbTarget1P) - % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC) - % "guard2" % "guard2" % "guard2" - % "result(variant2)" - ). str()); - - //Ann2 `result3` variant - transcend->addRawScript((formatLateAnnotation2 - % FORMATSYMBOL(symbTarget2P) - % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) - % "guard3" % "guard3" - % "result(variant3)" - ). str()); - - transcend->run(); - LateAnnotationsGroup group = transcend->queryLate("result"); - ASSERT_EQ(2, group.annotations.size()); - - for(const auto& annEntry: group.annotations){ - annEntry.first.print(cout); - cout <(transcend->parse(targetWrapped)); - if (targetSP == symbTarget1P){ - LateAnnotation ann = annEntry.second; - ASSERT_EQ(2, ann.guardedContent.size()); - - Expression selector(Operator::CALL, {Atom("guard2")}); - auto answer = ann.select({selector, selector, selector}man->root, man->transcend); - ASSERT_TRUE(answer); - string answerS = get<0>(transcend->parse(*answer)); - ASSERT_STREQ("variant2", answerS.c_str()); - - } else if (targetSP == symbTarget2P) { - LateAnnotation ann = annEntry.second; - ASSERT_EQ(1, ann.guardedContent.size()); - - Expression selector(Operator::CALL, {Atom("guard3")}); - auto answer = ann.select({selector, selector, selector}); - ASSERT_TRUE(answer); - ASSERT_STREQ("variant3", get<0>(transcend->parse(*answer)).c_str()); - - } else { - ASSERT_TRUE(false); - } - } -} - -TEST(LateReasoning, Compilation1){ - XreateManager* man = XreateManager::prepare(R"Code( -Color = type variant{RED, BLUE, GREEN}. - -test = function:: int; entry { - x = RED():: Color. - y1= switch late (x):: int - {0}. - y1 -} -)Code"); - - int (*program)() = (int (*)())man->run(); - int result = program(); - - ASSERT_EQ(0, result); -} - - diff --git a/cpp/tests/latetranscend.cpp b/cpp/tests/latetranscend.cpp new file mode 100644 index 0000000..51d3666 --- /dev/null +++ b/cpp/tests/latetranscend.cpp @@ -0,0 +1,208 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * latereasoning.cpp + * + * Author: pgess + * Created on April 21, 2018, 5:10 PM + */ + +#include "xreatemanager.h" +#include "transcendlayer.h" +#include "pass/latereasoningpass.h" +#include "aux/latereasoning.h" +#include "pass/dfapass.h" +#include +#include "gtest/gtest.h" + +using namespace std; +using namespace xreate; +using namespace xreate::latereasoning; + +TEST(LateReasoning, Syntax1) { + XreateManager* man = XreateManager::prepare(R"Code( +test = function:: int { + x = 0::int. + y1= switch late (x)::int{0}. + y2= switch late(x+y1->a::int)::int{1}. + y1+y2 +} +)Code"); + + CodeScope* scope = man->root->findFunction("test")->getEntryScope(); + Expression y1 = scope->getDefinition(scope->getSymbol("y1")); + Expression y2 = scope->getDefinition(scope->getSymbol("y2")); + + ASSERT_EQ(1, y1.bindings.size()); + ASSERT_STRCASEEQ("x", y1.bindings.at(0).c_str()); + + ASSERT_EQ(1, y2.bindings.size()); + ASSERT_STRCASEEQ("a", y2.bindings.at(0).c_str()); +} + +TEST(LateReasoning, Pass_DFAPassDec_1){ + typedef LateReasoningTranscendDecorator LRTranscend; + + auto man = details::tier2::XreateManager::prepare(R"Code( +Dom = type slave dom. + +test = function:: int; entry +{ + LateIdent = 0:: Dom. + 0:: int; ann1(LateIdent) +} +)Code"); + + CodeScope* scopeEntry = man->root->findFunction("test")->getEntryScope(); + ScopedSymbol keyS = scopeEntry->getSymbol("LateIdent"); + SymbolPacked keySP = man->transcend->pack(Symbol{keyS, scopeEntry}); + + std::shared_ptr scopeLateEntry(new LateReasoningScope(nullptr)); + scopeLateEntry->addIdentifier("LateIdent", keySP); + + typedef LateReasoningDFAPassDecorator LRDFAPass; + LRDFAPass* dfaPass = new LRDFAPass(man); + dfaPass->registerLateScope(scopeEntry, scopeLateEntry.get()); + + man->transcend->addRawScript("dom(guard1; guard2).\n"); + + man->registerPass(dfaPass, PassId::DFAPass, nullptr); + man->executePasses(); + + testing::internal::CaptureStdout(); + man->analyse(); + std::string outputActual = testing::internal::GetCapturedStdout(); + cout << outputActual << endl; + + string outputExpected = "late(s(0,-2,0), (s(1,-2,0)), (LateIdent), ann1(LateIdent)):- dom(LateIdent)."; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +//TEST(LateReasoning, Transcend_LRTransDec_1) { +// Attachments::init(); +// typedef LateReasoningTranscendDecorator LRTranscend; +// std::unique_ptr transcend(new LRTranscend()); + +// std::unique_ptr scope(new CodeScope(nullptr)); +// Symbol symbA = scope->addDefinition(Atom("a"), Expression()); +// Symbol symbB = scope->addDefinition(Atom("b"), Expression()); +// Symbol symbC = scope->addDefinition(Atom("c"), Expression()); +// Symbol symbTarget1 = scope->addDefinition(Atom("target1"), Expression()); +// Symbol symbTarget2 = scope->addDefinition(Atom("target2"), Expression()); + +// SymbolPacked symbpA = transcend->pack(symbA, "a"); +// SymbolPacked symbpB = transcend->pack(symbB, "b"); +// SymbolPacked symbpC = transcend->pack(symbC, "c"); +// SymbolPacked symbTarget1P = transcend->pack(symbTarget1, "target1"); +// SymbolPacked symbTarget2P = transcend->pack(symbTarget2, "target2"); + +// boost::format formatSymb("s(%1%,%2%,%3%)"); +// boost::format formatLateAnnotation1("late(%1%, (%2%, %3%, %4%), (%5%, %6%, %7%), %8%)."); +// boost::format formatLateAnnotation2("late(%1%, (%2%, %3%), (%4%, %5%), %6%)."); +// #define FORMATSYMBOL(s) (formatSymb % s.identifier % s.version % s.scope).str() + +// // Ann1, `variant1` +// transcend->addRawScript((formatLateAnnotation1 +// % FORMATSYMBOL(symbTarget1P) +// % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC) +// % "guard1" % "guard1" % "guard1" +// % "result(variant1)" +// ). str()); + +// //Ann1 `result2` variant +// transcend->addRawScript((formatLateAnnotation1 +// % FORMATSYMBOL(symbTarget1P) +// % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) % FORMATSYMBOL(symbpC) +// % "guard2" % "guard2" % "guard2" +// % "result(variant2)" +// ). str()); + +// //Ann2 `result3` variant +// transcend->addRawScript((formatLateAnnotation2 +// % FORMATSYMBOL(symbTarget2P) +// % FORMATSYMBOL(symbpA) % FORMATSYMBOL(symbpB) +// % "guard3" % "guard3" +// % "result(variant3)" +// ). str()); + +// transcend->run(); +// LateAnnotationsGroup group = transcend->queryLate("result"); +// ASSERT_EQ(2, group.annotations.size()); + +// for(const auto& annEntry: group.annotations){ +// annEntry.first.print(cout); +// cout <(transcend->parse(targetWrapped)); +// if (targetSP == symbTarget1P){ +// LateAnnotation ann = annEntry.second; +// ASSERT_EQ(2, ann.guardedContent.size()); + +// Expression selector(Operator::CALL, {Atom("guard2")}); +// auto answer = ann.select({selector, selector, selector}, man->root, man->transcend); +// ASSERT_TRUE(answer); +// string answerS = get<0>(transcend->parse(*answer)); +// ASSERT_STREQ("variant2", answerS.c_str()); + +// } else if (targetSP == symbTarget2P) { +// LateAnnotation ann = annEntry.second; +// ASSERT_EQ(1, ann.guardedContent.size()); + +// Expression selector(Operator::CALL, {Atom("guard3")}); +// auto answer = ann.select({selector, selector, selector}); +// ASSERT_TRUE(answer); +// ASSERT_STREQ("variant3", get<0>(transcend->parse(*answer)).c_str()); + +// } else { +// ASSERT_TRUE(false); +// } +// } +//} + +TEST(LateReasoning, Doc_SwitchLateOperation){ + auto man = details::tier2::XreateManager::prepare( +R"Code( + +mul = function(x::float, y::float):: float +{ + x * y +} + +ArithmeticT = type slave arithmetic. + +test = function(a::float, b::float, ArithmX::ArithmeticT):: float; entry +{ + switch late (ArithmX):: float + { + mul(a, b)::float; arithmetic(ArithmX) + } +} + +)Code"); + + string codeTranscend = +R"Code( + arithmetic(fast; accurate). +)Code"; + + typedef LateReasoningTranscendDecorator LateTranscend; + typedef LateReasoningDFAPassDecorator LTDFAPass; + + std::unique_ptr transcend(new LateTranscend()); + man->transcend = transcend.get(); + man->transcend->addRawScript(move(codeTranscend)); + LTDFAPass* pass = new LTDFAPass(man); + + man->registerPass(pass, PassId::DFAPass, nullptr); + man->executePasses(); + man->analyse(); + + auto result = transcend->queryLate("arithmetic"); + ASSERT_EQ(1, result.annotations.size()); + ASSERT_EQ(2, result.annotations.begin()->second.guardedContent.size()); + //man->run(); +} diff --git a/cpp/tests/latex.cpp b/cpp/tests/latex.cpp index 54ceff8..a8d3cec 100644 --- a/cpp/tests/latex.cpp +++ b/cpp/tests/latex.cpp @@ -1,298 +1,334 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Author: pgess * Created on June 25, 2018, 5:42 PM * * \file latex.cpp * \brief Testing of latex */ #include "xreatemanager.h" #include "pass/compilepass.h" #include "transcendlayer.h" #include "query/latex.h" #include "compilation/latex.h" #include "aux/xreatemanager-decorators.h" #include "compilation/scopedecorators.h" #include "llvmlayer.h" +#include "supplemental/docutils.h" #include #include using namespace xreate::latex; using namespace xreate::latereasoning; using namespace xreate::compilation; using namespace xreate; using namespace std; TEST(Latex, Script_NestedScopePropagation_1) { std::string program = R"CODE( import raw("scripts/cfa/context.lp"). fn = function:: int; entry { context:: test1. if(1==11)::int {2} else {3} } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* blockTrue = man->root->findFunction("fn")->getEntryScope()->getBody().blocks.front(); auto blockTrueP = man->transcend->pack(blockTrue); boost::format formatAlias("alias(%1%, %2%)."); man->transcend->addRawScript((formatAlias % blockTrueP % "block1").str()); man->transcend->addRawScript( R"SCRIPT( success1:- bind_scope(Block1, test1, strong); alias(Block1, block1). )SCRIPT"); man->analyse(); ASSERT_EQ(1, man->transcend->query("success1").size()); } TEST(Latex, Script_DemAndDecision_1) { std::string program = R"CODE( import raw("scripts/cfa/context.lp"). a = function:: int { context:: forC(a). c() } b = function:: int { context:: forC(b). c() } c = function:: int {0} main = function:: int; entry { a() + b() } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* blockC = man->root->findFunction("c")->getEntryScope(); auto blockCP = man->transcend->pack(blockC); boost::format formatAlias("alias(%1%, %2%)."); man->transcend->addRawScript((formatAlias % blockCP % "blockC").str()); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- alias(BlockC, blockC). latex_registered_subjects(forC, Variant):- bind_scope(_, Variant, strong); Variant = forC(_). )SCRIPT"); man->analyse(); ASSERT_EQ(1, man->transcend->query("latex_fn_demand").size()); ASSERT_EQ(2, man->transcend->query("latex_decision").size()); } TEST(Latex, LatexQuery_getFnDemand_1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). main = function:: int; entry { context:: alias(blockMain). 0 } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- bind_scope(BlockC, alias(blockMain), strong). latex_registered_subjects(forC, decisionSome). )SCRIPT"); LatexQuery* query = new LatexQuery(); man->transcend->registerQuery(query, QueryId::LatexQuery); man->analyse(); Demand demand = query->getFnDemand("main"); ASSERT_EQ(1, demand.size()); ASSERT_STREQ("forC", demand.front().c_str()); } TEST(Latex, LatexQuery_getDecision_static_1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). a = function:: int {context::decisionSome. c()} b = function:: int {c()} c = function:: int {context:: alias(blockC). 0} )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- bind_scope(BlockC, alias(blockC), strong). latex_registered_subjects(forC, decisionSome). )SCRIPT"); LatexQuery* query = new LatexQuery(); man->transcend->registerQuery(query, QueryId::LatexQuery); man->analyse(); LateAnnotation decisionLA = query->getDecision("forC", man->root->findFunction("a")->getEntryScope()); auto decisionGS = decisionLA.select({}, man->root, man->transcend); ASSERT_TRUE(decisionGS); decisionGS->print(cout); cout << endl; auto decisionTuple = man->transcend->parse(*decisionGS); string decision = get<2>(decisionTuple); ASSERT_STREQ("decisionSome", decision.c_str()); } TEST(Latex, Compilation_1) { std::string program = R"CODE( a = function:: int {0} main = function:: int; entry { a() } )CODE"; string script = R"SCRIPT( latex_fn_demand(%1%, subject1). latex_decision(%2%, subject1, 5). latex_registered_subjects(subject1, 1). latex_registered_subjects(subject1, 5). )SCRIPT"; typedef LatexBruteFunctionDecorator FnImpl; typedef LatexBruteScopeDecorator> ScopeImpl; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope()); boost::format format(script); man->transcend->addRawScript((format % "a" % scopeMainP).str()); man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); man->analyse(); std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); compiler->run(); man->llvm->initJit(); int(*fnMain)() = (int(*)())man->llvm->getFunctionPointer(compiler->getEntryFunction()); ASSERT_EQ(0, fnMain()); } // //TEST(Latex, Full1) { // std::string program = // R"CODE( // import raw("scripts/cfa/context.lp"). // // a = function:: int // { // context:: forC(a). // c() // } // // b = function:: int // { // context:: forC(b). // c() // } // // c = function:: int {0} // // main = function:: int; entry // { // a() + b() // } // )CODE"; // // string script = // R"SCRIPT( // alias(%1%, scopeC). // latex_scope_demand(ScopeC, forC) :- alias(ScopeC, scopeC). // latex_registered_subjects(forC, forC(a)). // latex_registered_subjects(forC, forC(b)). // )SCRIPT"; // // typedef LatexBruteFunctionDecorator FnImpl; // typedef LatexBruteScopeDecorator> ScopeImpl; // // std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); // ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope()); // auto scopeCP = man->transcend->pack(man->root->findFunction("c")->getEntryScope()); // boost::format format(script); // man->transcend->addRawScript((format %scopeCP).str()); // man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); // man->analyse(); // // std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); // compiler->run(); // man->llvm->print(); //} // TEST(Latex, Compilation_TransitFn1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). branchA = function:: int { context:: sink_a. fnTransit() } branchB = function:: int { context:: sink_b. fnTransit() } fnSink = function:: int {0} fnTransit = function:: int {fnSink()} main = function:: int; entry { branchA() + branchB() } )CODE"; string script = R"SCRIPT( alias(scopeSink, %1%). latex_scope_demand(ScopeSink, sink):- alias(scopeSink, ScopeSink). latex_registered_subjects(sink, sink_a). latex_registered_subjects(sink, sink_b). )SCRIPT"; typedef LatexBruteFunctionDecorator FnImpl; typedef LatexBruteScopeDecorator> ScopeImpl; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* scopeSink = man->root->findFunction("fnSink")->getEntryScope(); auto scopeSinkP = man->transcend->pack(scopeSink); ScopedSymbol argLatexSS{1, -1}; Symbol argLatexS{argLatexSS, scopeSink}; man->transcend->pack(argLatexS); boost::format format(script); man->transcend->addRawScript((format %scopeSinkP).str()); man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); man->analyse(); std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); compiler->run(); man->llvm->print(); man->llvm->initJit(); int(*fnMain)() = (int(*)()) man->llvm->getFunctionPointer(compiler->getEntryFunction()); int valueActual = fnMain(); ASSERT_EQ(0, valueActual); +} + +TEST(Latex, Doc_Examples1){ + std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Examples_1"); + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); +} + +TEST(Latex, Doc_Examples2){ + std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Examples_2"); + + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); +} + +TEST(Latex, Doc_ContextPropagation1){ + std::string program =getDocumentationExampleById("documentation/Concepts/context.xml", "ContextPropagation1"); + + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); +} + +TEST(Latex, Doc_ContextPropagation2){ + std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "ContextPropagation2"); + + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); +} + +TEST(Latex, Doc_ContextPropagation3){ + std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "ContextPropagation3"); + + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); +} + +TEST(Latex, Doc_Latex1){ + std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Latex1"); + + std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); } \ No newline at end of file diff --git a/cpp/tests/modules.cpp b/cpp/tests/modules.cpp index 602e096..e9820aa 100644 --- a/cpp/tests/modules.cpp +++ b/cpp/tests/modules.cpp @@ -1,407 +1,371 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * modules.cpp * * Author: pgess * Created on June 18, 2017, 8:25 PM */ class Modules_AST2_Test; class Modules_Discovery1_Test; class Modules_Solve1_Test; #define FRIENDS_MODULES_TESTS \ friend class ::Modules_AST2_Test; \ friend class ::Modules_Discovery1_Test; \ friend class ::Modules_Solve1_Test; #include "modules.h" #include "aux/xreatemanager-decorators.h" #include "aux/xreatemanager-modules.h" #include "xreatemanager.h" #include "modules/Parser.h" +#include "supplemental/docutils.h" #include "gtest/gtest.h" #include #include #include namespace fs = boost::filesystem; using namespace std; using namespace xreate; using namespace xreate::modules; TEST(Modules, AST1) { FILE* input = fopen("scripts/dsl/regexp.xreate","r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); ASSERT_EQ(parser.errors->count, 0); } TEST(Modules, AST2){ string code = R"Code( module:: name(test1); status(untested) { require(provides(logging)). controller("/tmp/test-controller.ls"). discover("/tmp/root/"). } )Code"; Scanner scanner(reinterpret_cast(code.c_str()), code.size()); Parser parser(&scanner); parser.Parse(); ModuleRecord module = parser.module; ASSERT_EQ(2, module.__properties.size()); ASSERT_EQ("name", module.__properties.front().getValueString()); ASSERT_EQ("status", module.__properties.back().getValueString()); ASSERT_EQ(1, module.__requests.size()); ASSERT_EQ("provides", module.__requests.front().getValueString()); ASSERT_EQ(1, module.__controllers.size()); ASSERT_EQ("/tmp/test-controller.ls", module.__controllers.front()); ASSERT_EQ(1, module.__discoveryPaths.size()); ASSERT_EQ("/tmp/root/", module.__discoveryPaths.front()); } TEST(Modules, Discovery1){ const std::string dirModulesRoot = "/tmp/testModulesDiscovery1_t54723/"; string codeA = R"Code( module::name(testA); status(needToTestMore). )Code"; string codeB = R"Code( module:: name(testB); status(needToTestEvenMore). )Code"; string codeMain = string("module{discover (\"") + dirModulesRoot + "\").}"; fs::create_directories(dirModulesRoot); fs::ofstream fileA(dirModulesRoot + "a.xreate"); fileA << codeA; fileA.close(); fs::ofstream fileB(dirModulesRoot + "b.xreate"); fileB << codeB; fileB.close(); Scanner scanner(reinterpret_cast(codeMain.c_str()), codeMain.size()); Parser parser(&scanner); parser.Parse(); ModulesSolver solver; solver.discoverModules(parser.module); fs::remove_all(dirModulesRoot); std::string output = solver.__program.str(); cout << output << endl; ASSERT_NE(string::npos, output.find("bind_module(\"/tmp/testModulesDiscovery1_t54723/a.xreate\", name(testA)).")); ASSERT_NE(string::npos, output.find("bind_module(\"/tmp/testModulesDiscovery1_t54723/b.xreate\", status(needToTestEvenMore)).")); } TEST(Modules, Requests1){ } -TEST(Modules, Doc_AdvModRes_1){ - string codeA = - R"Code( - //First Module - module:: - name(testA); - provide(superService); - status(needToTestEvenMore). - )Code"; - - string codeB = - R"Code( - //Second Module - module:: - name(testB); - provide(superService); - status(needToTest). - )Code"; - - string codeMain = -R"Code( - //Third Module - module { - require (superService). - - discover("/tmp/testModulesDiscovery1_t54724/"). - controller("/tmp/testModulesDiscovery1_t54724/controller"). - } -)Code"; - - const std::string dirModulesRoot = "/tmp/testModulesDiscovery1_t54724/"; - - string codeController = -R"Code( -status_score(0, needToTestEvenMore). -status_score(1, needToTest). - -module_include_candidate(Request, Y):- - bind_module(Y, provide(Request)). - -module_include_winner(Request, MaxScore) :- - MaxScore = #max{Score: module_include_candidate(Request, Y), bind_module(Y, status(Status)), status_score(Score, Status)}; - modules_require(_, Request). - -modules_resolution(Request, Y) :- - module_include_winner(Request, MaxScore); - bind_module(Y, provide(Request)); - bind_module(Y, status(Status)); - status_score(MaxScore, Status). -)Code"; - - fs::create_directories(dirModulesRoot); - fs::ofstream fileA(dirModulesRoot + "a.xreate"); - fileA << codeA; - fileA.close(); - - fs::ofstream fileB(dirModulesRoot + "b.xreate"); - fileB << codeB; - fileB.close(); - - fs::ofstream fileController(dirModulesRoot + "controller"); - fileController << codeController; - fileController.close(); - - Scanner scanner(reinterpret_cast(codeMain.c_str()), codeMain.size()); - Parser parser(&scanner); - parser.Parse(); - - ModulesSolver solver; - solver.init("", parser.module); - fs::remove_all(dirModulesRoot); - - cout << solver.__program.str() << endl; - std::list modulesRequired = solver.run(parser.module); - - ASSERT_EQ(1, modulesRequired.size()); - string moduleActualRequired = modulesRequired.front(); - - string moduleExpected = dirModulesRoot + "b.xreate"; - ASSERT_EQ(moduleExpected, moduleActualRequired); -} - TEST(Modules, Compilation1){ const std::string dirModulesRoot = "/tmp/testModulesDiscovery1_t54726/"; string codeMain = R"Code( module { discover("/tmp/testModulesDiscovery1_t54726/"). controller("/tmp/testModulesDiscovery1_t54726/controller"). require (superService). } test = function:: int; entry { getYourNumber() } )Code"; string codeA = R"Code( module:: name(testA); provide(superService); status(needToTestEvenMore). getYourNumber= function:: int {0} )Code"; string codeB = R"Code( module :: name(testB); provide(superService); status(needToTestMore). getYourNumber= function:: int {1} )Code"; string codeController = R"Code( status_score(0, needToTestEvenMore). status_score(1, needToTestMore). module_include_candidate(Request, Y):- bind_module(Y, provide(Request)). module_include_winner(Request, MaxScore) :- MaxScore = #max{Score: module_include_candidate(Request, Y), bind_module(Y, status(Status)), status_score(Score, Status)}; modules_require(_, Request). modules_resolution(Request, Y) :- module_include_winner(Request, MaxScore); bind_module(Y, provide(Request)); bind_module(Y, status(Status)); status_score(MaxScore, Status). )Code"; fs::create_directories(dirModulesRoot); fs::ofstream fileA(dirModulesRoot + "a.xreate"); fileA << codeA; fileA.close(); fs::ofstream fileB(dirModulesRoot + "b.xreate"); fileB << codeB; fileB.close(); fs::ofstream fileController(dirModulesRoot + "controller"); fileController << codeController; fileController.close(); auto man = new XreateManagerImpl>(); man->prepareCode(std::move(codeMain)); fs::remove_all(dirModulesRoot); int (*funcMain)() = (int (*)()) man->run(); int result = funcMain(); ASSERT_EQ(1, result); } TEST(Modules, Compilation_AssignModulePath1){ const std::string dirModulesRoot = "/tmp/testModulesDiscovery1_t54725/"; string codeMain = R"Code( module { discover("/tmp/testModulesDiscovery1_t54725/"). controller("/tmp/testModulesDiscovery1_t54725/controller"). require (superService). } test = function:: int; entry { getYourNumber() } )Code"; string codeA = R"Code( module:: name(testA); provide(superService); status(needToTestEvenMore). getYourNumber= function:: int {0} )Code"; string codeController = R"Code( modules_resolution(superService, "/tmp/testModulesDiscovery1_t54725/a.xreate"). )Code"; fs::create_directories(dirModulesRoot); fs::ofstream fileA(dirModulesRoot + "a.xreate"); fileA << codeA; fileA.close(); fs::ofstream fileController(dirModulesRoot + "controller"); fileController << codeController; fileController.close(); auto man = new XreateManagerImpl>(); man->prepareCode(std::move(codeMain)); fs::remove_all(dirModulesRoot); int (*funcMain)() = (int (*)()) man->run(); int result = funcMain(); ASSERT_EQ(0, result); } TEST(Modules, Doc_Requesting_Modules_1){ - string codeExcerpt = R"Code( - module { - require(logger). - } - )Code"; + string codeExcerpt = getDocumentationExampleById("documentation/Syntax/modules.xml", "Requesting_Modules_1"); - string codeController = R"Code( - modules_resolution(logger, "/tmp/logger"). - )Code"; + string codeController = R"Code( + modules_resolution(logger, "/tmp/logger"). + )Code"; - Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); - Parser parser(&scanner); - parser.Parse(); + Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); + Parser parser(&scanner); + parser.Parse(); - ModulesSolver solver; - solver.init(move(codeController), parser.module); - std::list result = solver.run(parser.module); - ASSERT_EQ(1, result.size()); - ASSERT_STREQ("/tmp/logger", result.front().c_str()); + ModulesSolver solver; + solver.init(move(codeController), parser.module); + std::list result = solver.run(parser.module); + ASSERT_EQ(1, result.size()); + ASSERT_STREQ("/tmp/logger", result.front().c_str()); } TEST(Modules, Doc_Requesting_Modules_2){ - string codeExcerpt = R"Code( - module{require(stringslib).} - processString = function(a:: string):: string - { - someStrFunc(a) - } - - module{require(mathlib).} - processNumber = function(a:: num):: num - { - someMathFunc(a) - } - )Code"; - - string codeController = R"Code( - modules_resolution(stringslib, "/tmp/stringslib"). - modules_resolution(mathlib, "/tmp/mathlib"). - )Code"; - - Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); - Parser parser(&scanner); - parser.Parse(); - - ModulesSolver solver; - solver.init(move(codeController), parser.module); - std::list result = solver.run(parser.module); - ASSERT_EQ(2, result.size()); - ASSERT_STREQ("/tmp/stringslib", result.front().c_str()); - ASSERT_STREQ("/tmp/mathlib", result.back().c_str()); + string codeExcerpt = getDocumentationExampleById("documentation/Syntax/modules.xml", "Requesting_Modules_2"); + + string codeController = R"Code( + modules_resolution(stringslib, "/tmp/stringslib"). + modules_resolution(mathlib, "/tmp/mathlib"). + )Code"; + + Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); + Parser parser(&scanner); + parser.Parse(); + + ModulesSolver solver; + solver.init(move(codeController), parser.module); + std::list result = solver.run(parser.module); + ASSERT_EQ(2, result.size()); + ASSERT_STREQ("/tmp/stringslib", result.front().c_str()); + ASSERT_STREQ("/tmp/mathlib", result.back().c_str()); } TEST(Modules, Doc_ModuleAnnotations_1){ - string codeExcerpt = R"Code( - module:: status(obsolete). - )Code"; + string codeExcerpt = getDocumentationExampleById("documentation/Syntax/modules.xml", "ModuleAnnotations_1"); - Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); - Parser parser(&scanner); - parser.Parse(); + Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); + Parser parser(&scanner); + parser.Parse(); - ModulesSolver solver; - solver.init("", parser.module); + ModulesSolver solver; + solver.init("", parser.module); - string program = solver.__program.str(); - ASSERT_NE(string::npos, program.find("bind_module(\"\", status(obsolete)).")); + string program = solver.__program.str(); + ASSERT_NE(string::npos, program.find("bind_module(\"\", status(obsolete)).")); } TEST(Modules, Doc_ModulesResolution_1){ - string codeExcerpt = R"Code( - modules_resolution(numlib, "/path/to/numlib"). - modules_resolution(strings, "/path/to/ansi-lib", ""). - modules_resolution(strings, "/path/to/utf8-lib", "moduleB"). - )Code"; + string codeExcerpt = getDocumentationExampleById("documentation/Syntax/modules.xml", "ModulesResolution_1"); string codeModule = R"Code( module {require(numlib). require(strings)} )Code"; + //use ModuleA as a current module + codeExcerpt.replace(codeExcerpt.find("moduleA"), 7, ""); + Scanner scanner(reinterpret_cast(codeModule.c_str()), codeModule.size()); Parser parser(&scanner); parser.Parse(); ModulesSolver solver; solver.init(codeExcerpt, parser.module); std::list result = solver.run(parser.module); ASSERT_EQ(2, result.size()); ASSERT_STREQ("/path/to/numlib", result.front().c_str()); ASSERT_STREQ("/path/to/ansi-lib", result.back().c_str()); } + +TEST(Modules, Doc_AdvModRes_1){ + string code = getDocumentationExampleById("documentation/Syntax/modules.xml", "AdvModRes_1"); + size_t posCodeA = code.find("//First Module"); + size_t posCodeB = code.find("//Second Module"); + size_t posCodeMain = code.find("//Third Module"); + string codeA = code.substr(posCodeA, posCodeB - posCodeA); + string codeB = code.substr(posCodeB, posCodeMain - posCodeB); + string codeMain = code.substr(posCodeMain); + + const std::string dirModulesRoot = "/tmp/testModulesDiscovery1_t54724/"; + replace(codeMain, string("/modules/path/"), dirModulesRoot); + replace(codeMain, string("/path/to/controller"), dirModulesRoot + "controller"); + cout << codeMain << endl; + + string codeController = +R"Code( +status_score(0, needToTestEvenMore). +status_score(1, needToTest). + +module_include_candidate(Request, Y):- + bind_module(Y, provide(Request)). + +module_include_winner(Request, MaxScore) :- + MaxScore = #max{Score: module_include_candidate(Request, Y), bind_module(Y, status(Status)), status_score(Score, Status)}; + modules_require(_, Request). + +modules_resolution(Request, Y) :- + module_include_winner(Request, MaxScore); + bind_module(Y, provide(Request)); + bind_module(Y, status(Status)); + status_score(MaxScore, Status). +)Code"; + + fs::create_directories(dirModulesRoot); + fs::ofstream fileA(dirModulesRoot + "a.xreate"); + fileA << codeA; + fileA.close(); + + fs::ofstream fileB(dirModulesRoot + "b.xreate"); + fileB << codeB; + fileB.close(); + + fs::ofstream fileController(dirModulesRoot + "controller"); + fileController << codeController; + fileController.close(); + + Scanner scanner(reinterpret_cast(codeMain.c_str()), codeMain.size()); + Parser parser(&scanner); + parser.Parse(); + + ModulesSolver solver; + solver.init("", parser.module); + fs::remove_all(dirModulesRoot); + + cout << solver.__program.str() << endl; + std::list modulesRequired = solver.run(parser.module); + + ASSERT_EQ(1, modulesRequired.size()); + string moduleActualRequired = modulesRequired.front(); + + string moduleExpected = dirModulesRoot + "b.xreate"; + ASSERT_EQ(moduleExpected, moduleActualRequired); +} \ No newline at end of file diff --git a/cpp/tests/polymorph.cpp b/cpp/tests/polymorph.cpp index 9835ac1..5a930bc 100644 --- a/cpp/tests/polymorph.cpp +++ b/cpp/tests/polymorph.cpp @@ -1,147 +1,160 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * polymorph.cpp * * Author: pgess * Created on October 11, 2017, 8:37 PM */ #include "xreatemanager.h" #include "ast.h" #include "transcendlayer.h" #include "aux/latereasoning.h" #include #include "gtest/gtest.h" #include "query/polymorph.h" +#include "supplemental/docutils.h" using namespace xreate; using namespace xreate::latereasoning; using namespace xreate::polymorph; using namespace std; TEST(Polymorphs, ast1) { xreate::XreateManager* man = xreate::XreateManager::prepare( R"CODE( guard:: a { test = function:: int {0} } guard:: b { test = function:: int {1} } main = function:: int; entry { test() } )CODE"); const std::list& specs = man->root->getFunctionSpecializations("test"); ASSERT_EQ(2, specs.size()); auto itSpecs = specs.begin(); ASSERT_EQ("a", (*itSpecs)->guard.getValueString()); itSpecs++; ASSERT_EQ("b", (*itSpecs)->guard.getValueString()); } TEST(Polymorphs, PolymorphQuery_Static_1) { xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( R"CODE( import raw("scripts/dfa/polymorphism.lp"). guard:: a { test = function:: int {0} } guard:: b { test = function:: int {1} } main = function:: int; entry { test()::int; callguard(b); dfa_polym(ret)} )CODE"); man->analyse(); PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); const Expression& bodyE = man->root->findFunction("main")->getEntryScope()->getBody(); LateAnnotation decisionLA = query->get(bodyE); ASSERT_EQ(1, decisionLA.guardedContent.size()); auto decisionOptSymb = decisionLA.select({}, man->root, man->transcend); ASSERT_TRUE(decisionOptSymb); decisionOptSymb->print(cout); cout << endl; string guard = query->getValue(*decisionOptSymb).getValueString(); ASSERT_STREQ("b", guard.c_str()); } TEST(Polymorphs, PolymorphQuery_Late_1){ xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( R"CODE( Late = type variant{a, b}. main = function:: int; entry{key= a():: Late; test. key::int} )CODE"); man->transcend->addRawScript( R"RULE( late(S, S, a, dfa_callguard(S, a)):- bind(S, test). late(S, S, b, dfa_callguard(S, b)):- bind(S, test). )RULE"); man->analyse(); PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); CodeScope* scopeMain = man->root->findFunction("main")->getEntryScope(); Symbol keyS = Symbol{scopeMain->getSymbol("key"), scopeMain}; Expression keyE = scopeMain->getDefinition(keyS); latereasoning::LateAnnotation answerLA = query->get(keyE); Expression valueB(Operator::VARIANT, {}); valueB.setValueDouble(1); auto answerRaw = answerLA.select({valueB}, man->root, man->transcend); ASSERT_TRUE(answerRaw); Expression answerE = query->getValue(*answerRaw); ASSERT_STREQ("b", answerE.getValueString().c_str()); } TEST(Polymorphs, PSCU_1){ auto man = details::tier1::XreateManager::prepare(R"Code( Dom = type variant {guard1, guard2}. guard:: guard1 { compute = function :: int {0} } guard:: guard2 { compute = function :: int {1} } test = function:: int; entry { xLate = guard2():: Dom. y1= switch late (xLate:: Dom; alias(xLate)):: int { compute():: int; guardkey(xLate) }. y1 } )Code"); man->transcend->addRawScript(R"RAW( dom(guard1; guard2). late(Target, Key, Variant, dfa_callguard(Target, Variant)):- bind(Target, guardkey(Alias)); bind(Key, alias(Alias)); dom(Variant). )RAW"); man->analyse(); int (*program)() = (int (*)())man->run(); int result = program(); ASSERT_EQ(1, result); } + +TEST(Polymorphs, Doc_FnLvlPoly_1){ + string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "FnLvlPoly_1"); + auto man = XreateManager::prepare(move(example)); + ASSERT_TRUE(true); +} + +TEST(Polymorphs, Doc_LatePoly_1){ + string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "LatePoly_1"); + auto man = XreateManager::prepare(move(example)); + ASSERT_TRUE(true); +} \ No newline at end of file diff --git a/cpp/tests/supplemental/docutils.cpp b/cpp/tests/supplemental/docutils.cpp new file mode 100644 index 0000000..627daf4 --- /dev/null +++ b/cpp/tests/supplemental/docutils.cpp @@ -0,0 +1,54 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * \file docutils.cpp + * \brief Documentation processing + * + */ + +#include "docutils.h" + +#include +#include +#include +#include +#include + +using namespace std; + +std::string +getDocumentationExampleById(std::string filename, std::string id) { + string query = string("(//db:programlisting | //db:code)[@xml:id='") + id + "']"; + xmlDocPtr doc = xmlParseFile(filename.c_str()); + if (!doc) { + cout << "Can't find documentation file: " << filename << endl; + assert(false); + return ""; + } + + xmlXPathContextPtr context = xmlXPathNewContext(doc); + xmlXPathRegisterNs(context, BAD_CAST "db", BAD_CAST "http://docbook.org/ns/docbook"); + + xmlXPathObjectPtr response = xmlXPathEvalExpression((xmlChar*) query.c_str(), context); + xmlXPathFreeContext(context); + xmlNodeSetPtr nodeset = response->nodesetval; + + if (nodeset->nodeNr != 1) { + cout << "Can't find specified example " << id << endl; + assert(false); + return ""; + } + + xmlChar* contentC = xmlNodeListGetString(doc, nodeset->nodeTab[0]->xmlChildrenNode, 1); + string content = (char*) contentC; + xmlFree(contentC); + xmlXPathFreeObject (response); + + assert(content.size() && "Empty example"); + return content; +} + +void +replace(std::string& str, const std::string& subA, const std::string& subB) { + str.replace(str.find(subA), subA.size(), subB); +} \ No newline at end of file diff --git a/cpp/tests/supplemental/docutils.h b/cpp/tests/supplemental/docutils.h new file mode 100644 index 0000000..c6c50f4 --- /dev/null +++ b/cpp/tests/supplemental/docutils.h @@ -0,0 +1,21 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * File: docuils.h + * Author: pgess + * + * Created on November 29, 2018, 3:58 PM + * + * \brief Documentation processing\ + */ + +#ifndef DOC_H +#define DOC_H + +#include + +std::string getDocumentationExampleById(std::string filename, std::string id); +void replace(std::string& str, const std::string& subA, const std::string& subB); + +#endif /* DOC_H */ + diff --git a/cpp/tests/transcend.cpp b/cpp/tests/transcend.cpp index 5ad0ef3..7795d83 100644 --- a/cpp/tests/transcend.cpp +++ b/cpp/tests/transcend.cpp @@ -1,43 +1,58 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Author: pgess * Created on June 7, 2018, 3:35 PM * * \file transcend.cpp * \brief Transcend's tests */ #include "xreatemanager.h" #include "transcendlayer.h" +#include "supplemental/docutils.h" #include using namespace xreate; using namespace std; TEST(Transcend, Parse1) { - std::string script = -R"Code( + std::string script = + R"Code( )Code"; - std::unique_ptr man(details::tier1::XreateManager::prepare(std::move(script))); + std::unique_ptr man(details::tier1::XreateManager::prepare(std::move(script))); - std::string scriptTranscend = -R"Code( + std::string scriptTranscend = + R"Code( test1((1)). test2((1, 2)). )Code"; - man->transcend->addRawScript(move(scriptTranscend)); - man->analyse(); + man->transcend->addRawScript(move(scriptTranscend)); + man->analyse(); - StaticModel solution = man->transcend->query("test1"); - Gringo::Symbol symbTest1 = solution.begin()->second; - auto answer1 = man->transcend->parse>(symbTest1); - ASSERT_EQ(1, get<0>(answer1).size()); + StaticModel solution = man->transcend->query("test1"); + Gringo::Symbol symbTest1 = solution.begin()->second; + auto answer1 = man->transcend->parse>(symbTest1); + ASSERT_EQ(1, get<0>(answer1).size()); - solution = man->transcend->query("test2"); - Gringo::Symbol symbTest2 = solution.begin()->second; - auto answer2 = get<0>(man->transcend->parse>(symbTest2)); - ASSERT_EQ(2, answer2.size()); + solution = man->transcend->query("test2"); + Gringo::Symbol symbTest2 = solution.begin()->second; + auto answer2 = get<0>(man->transcend->parse>(symbTest2)); + ASSERT_EQ(2, answer2.size()); +} + +TEST(Transcend, Doc_Expressions1) { + string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Expressions1"); + XreateManager::prepare(move(code)); + + ASSERT_TRUE(true); +} + +TEST(Transcend, Doc_Codeblocks1) { + string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Codeblocks1"); + XreateManager::prepare(move(code)); + + ASSERT_TRUE(true); } \ No newline at end of file diff --git a/documentation-api/latereasoning.graphml b/documentation-api/latetranscend.graphml similarity index 74% rename from documentation-api/latereasoning.graphml rename to documentation-api/latetranscend.graphml index 25c65a6..808e3a4 100644 --- a/documentation-api/latereasoning.graphml +++ b/documentation-api/latetranscend.graphml @@ -1,552 +1,550 @@ - + - Late Reasonng Diagram - Pass - Interpretation - + Late Transcend Diagram + Pass + Interpretation + - Transcend + Transcend - + - + - + - + - LateReasoning + LateReasoning Scope - fvffdfd - vdffldkjfd + fvffdfd + vdffldkjfd fdfdfd - + - LateReasoning + LateReasoning DFAPass Decorator - fvffdfd - vdffldkjfd + fvffdfd + vdffldkjfd fdfdfd registerLateScope processAnnotations - + - DFAPass + DFAPass - + - ICodeScope + ICodeScope - + - LateSymbol -Recognized + LateParameter - + - DFAGraph + DFAGraph - + - LateReasoning + LateReasoning Compiler processSwitchLateStatement compileAutoExpand findKeys - + - LateReasoning + LateReasoning Transcend Decorator queryLate processSolution - + - Transcend + Transcend Layer - + - LateAnnotations + LateAnnotations Group select() - + - Late + Late Annotation - + - Transcend + Transcend program - + - DFA + DFA PassDec - + - Interpretation + Interpretation Scope - + - LR + LR Trans Dec - + - AST + AST getType - fvffdfd - vdffldkjfd + fvffdfd + vdffldkjfd fdfdfd - + - dereferenceSlaveType + dereferenceSlaveType - + + + - - - - + - - + + - - + + - - + + - + - + + + - + - - - + diff --git a/documentation-api/latex.graphml b/documentation-api/latex.graphml index 818a882..96adb5a 100644 --- a/documentation-api/latex.graphml +++ b/documentation-api/latex.graphml @@ -1,819 +1,819 @@ - Latex - Compilation - Transcend - Interpretation + Latex + Compilation + Transcend + Interpretation - ExtraArgs + ExtraArgs FnInvocation - Latex + Latex BruteScope Dec - ICodeScopeUnit + ICodeScopeUnit - Latex + Latex BruteFunction Dec - IFunctionUnit + IFunctionUnit - getSubjectDomain + getSubjectDomain - Latex + Latex Query - IQuery + IQuery - collapseColumn + collapseColumn - Latex + Latex Query - represent + represent Trans Expression - + - Latex script + Latex script - + - Latex script + Latex script - + - Latex - Output - Processing - Input + Latex + Output + Processing + Input - + - + - + - Demand + Demand upstream - + - scope_demand + latex_scope_demand - latex_registered_subjects + latex_registered_subjects - scope_demand(1) + scope_demand(1) - fn_demand + fn_demand - decision + decision - scope_demand(2) + scope_demand(2) - late(decision) + late(decision) - scope_fn + scope_fn - fn_demand_ordered + fn_demand_ordered - latex_symbol + latex_symbol diff --git a/documentation/Concepts/context.xml b/documentation/Concepts/context.xml new file mode 100644 index 0000000..8f9fc49 --- /dev/null +++ b/documentation/Concepts/context.xml @@ -0,0 +1,311 @@ + + + Context + + Computer program and its internal states and transitions between them + can be looked at from two different points of view: control flow and data + flow. Any information that can be derived from control flow is called + context in Xreate. + + Data that can be captured from analysing control flow consists of two + parts as follows: + + + + Instantaneous state or current place within the code. It's fully + determined by current code block as well as hierarchy of all its + respective parents + + + + Historical data determined by all previous visited code + blocks + + + + Xreate allows to express Context and reason about it by employing + block level annotations. See syntax. + +
+ Examples of Context Usage: Suggestions and Requirements + + //someStringFn = function:: string {...} + +main = function:: string; entry +{ + context:: env(utf8). + someStringFn() +} + + Example shows annotation env(utf8) which conveys some + information about the block thus distinguishing it from the others. It + allows compiler to apply specific compilation rules for this block. + Suppose someStringFn has different specializations for + different environments. Now it's possible to invoke specialization + tailored for UTF8 environment. + + Different story with the next example. Here we want to stipulate + context properties: + + guard:: safe +{ + crucialOperation = function:: int + {0} +} + +main = function:: int; entry +{ + context:: env(safe). + crucialOperation() +} + + Function crucialOperation has only one specialization + safe in the example. If context does not provide required + environment env(safe)compiler can't find appropriate + specialization and halts with compilation error. This is a way for + function to express requirements or contract to a + context it works within. +
+ +
+ Context Propagation + + Context of a particular code block contains not only its own + annotations but also reflects parent blocks as well as previously executed + blocks. It's achieved by context propagation. + + Context propagation means that nested blocks + inherit context of parents. Moreover callee + function's context inherits caller's one. Example: + + //requires 'safe' context +guard:: safe +{ + crucialOperation = function(a:: int, b::int):: int + { 0 } +} + +test = function:: int; entry { + //blockA + context:: env(safe). + + range = [1..10]:: [int]. + loop fold(range->x::int, 0->acc):: int { + //blockB + crucialOperation(x, acc) // In the nested scope env(safe) context still accessible + } +} + + This is example of nested scope context + propagation. It demonstrates availability of a + env(safe) annotation in the context of the nested block + blockB despite being declared in blockA. + + More complicated case is inter-function context + propagation. It means context propagates through + "caller/callee" relation: callee inherits caller context. The example + below demonstrates this: + + toSI = function(x:: int):: int + { 0 } + +calculate = function(x:: int):: int +{ + y = toSI(x):: int. + y +} + +test = function:: int; entry +{ + context:: units(millimeters). + calculate(10) +} + + Suppose calculate()works with values measured in + different units. It normalizes each value by invoking toSI() + conversion. One approach is to keep unit information for each variable + independently. But if we know that entire program or a part of it works + only with specific unit we can register it in a context, + units(millimeters)in this example, and + calculate() and its callees inherit context allowing compiler + to generate code tailored for specific units only. + + Context is determined by reasoning over control flow graph of a + program during compilation. Let's consider example: + + calcInches = function:: int +{ + context:: precision(accurate); units(inches). + calculate() +} + +calcMillis = function:: int +{ + context:: precision(accurate); units(millimeters). + calculate() +} + +calculate = function:: int + { 0 } + + Client functions calcInches() and + calcMillis() each define context with a configuration options + for a main routine calculate(). Unlike in previous example, + there are several callers with different context here. + + In case with several possible control flow paths each introducing + different context, only path invariant context annotations could be + determined at compile time. Obviously, annotations that are the same for + each possible alternative are part of context in any case. It's + precision(accurate) in the example above, since both client + function define it. More formally, statically determined context is a + conjunction of all possible contexts of a given code block. + + However the other annotation units(...) differs from + path to path and can be determined only during runtime. Late Transcend + functionality is used for this. Context reasoning employing Late Transcend + called Late Context or Latex for + short. +
+ +
+ Latex (Late Context) + + Static(compile-time) context reasoning is weak + since it's able to infer only partial context, consisting of properties + that are true for all possible paths leading in a CFG to a given block. + Other part consists of set of possible properties that depends on exact + path in CFG. Such uncertainty possible to resolve during runtime once it's + known which path is chosen. + + It leads to a necessity of having //late context// - context data + gathered on relevant occasion at runtime to determine right + decisions. + + However, for any cases it's crucial to consider //possible + contexts// that is, contexts valid only under certain conditions. + + Latex approach can be described as follows: + + + + All possible alternative contexts for the given scope computed + during compile time used as input for Latex + + + + All possible paths are numerated and specific latex parameter + created to keep data about current path. + + + + Late parameter used as guard for Late Transcend facts context + consists of. + + + + As of now, to convey late context data latex parameter injected into + function signature as hidden parameter. + + import raw ("core/control-context.lp"). + +compute = function:: int + { 0 } + +computeFast = function:: int { + context:: computation(fast). + + compute() +} + +computePrecisely = function:: int { + context:: computation(precise). + + compute() +} + +test = function(cmnd:: int):: int; entry { + context:: arithmetic(iee754). + + if (cmnd > 0)::int {computePrecisely()} else {computeFast()} +} + + Static scope + + + + import raw ("core/control-context.lp") +case context:: computation(fast) { + compute = function:: num { + 0 + } +} + +case context:: computation(precise) { + compute = function:: num { + 0 + } +} + +executeComputation= function:: num { + compute() +} + +test = function(cmnd:: num):: num; entry { + if (cmnd > 0)::num { + context:: computation(fast). + executeComputation() + + } else { + context:: computation(precise). + executeComputation() + } +} + + To sum up, context consists of two complements parts: on the one + hand //static(early) context// denotes compile time inferences, + + and on the other hand, //late(dynamic) context// denotes annotations + decided upon at runtime. + + + Since it is possible to determine number of possible contexts with + diffent outcome decisions, it is possible to determine least size for + late context data enough to identify each possible variant. (In example + above, since there are only two specializons of `compute`, 1 bit is + enough to convey late context data) + +
+ +
+ Remarks on late context implementation + + + + To return to a last example, in order to correcly determine + `compute`'s context it's necessary: + + After such transformation signature of `executeComputation` looks + like `executeComputation(__hidden_context_data__)`, + + where `hidden_context_data` holds data enough to determine within + `executeComputation` which one of possible contexts it encountered + with. + + Consequently, `executeComputation` decides which specialization of + `compute` should be called based on `hidden_context_data` value. + + Only at run-time there is enough information for + `executeComputation` to decide what specialization of `compute` to + call. +
+
diff --git a/documentation/Concepts/polymorphism.xml b/documentation/Concepts/polymorphism.xml new file mode 100644 index 0000000..d1e0674 --- /dev/null +++ b/documentation/Concepts/polymorphism.xml @@ -0,0 +1,182 @@ + + + Polymorphism + + Polymorphism is an umbrella term to denote number + of techniques across different programing paradigms. They all share the same + intention to provide ability easily recombine software components in a + different way with as little as possible manual work on developer's side. It + serves two major goals: specialization, when software, + initially designed to support wide range of use cases, is configured for + concrete particular case and extension - adapting + software to an environment and conditions it was not specifically designed + for. + + In course of software engineering evolution, number of polymorphism + techniques was proposed and experimented with, all suited for different + use-cases. Xreate presents generalized and elegant approach that + exhaustively covers wide landscape of polymorphism variations. + + Polymorphism in Xreate can be applied on two levels: + + + + Functions level. Function in Xreate can have + a multiple specializations and polymorphism is + compiler's ability to decide which exactly specialization to use + depending on various factors + + + + Modules level. Multiple modules can provide + the same service for users. Modules + Resolution is a process to decide which exactly module to + use + + + +
+ Function Level Polymorphism + + Basic idea is to allow developer to define several functions with + the same name or, in other words, several + specializations. Caller code then invokes necessary + function by its shared name but can't directly specify particular + specialization. Exact specialization to be invoked is decided later by + decision process called polymorphism resolution + carried out by Transcend. This indirect invocation approach gives enough + flexibility to use or replace different specializations depending on + various conditions during compile time as well as at runtime. + + Please refer to syntax for details + about function specializations. Each specialization must have unique + guard(among all specializations with the same name) + to be discernible from others. To summarize, function invocation is a two + layered process, in which client code specifies callee function's shared + name, and polymorphism resolution specifies specialization guard if + needed. + + For an example, assume that we develop program to operate under + specified time constraints. To model implementation suitable for real time + environment, one specialization of crucialOperation is + defined with env(realtime) guard i.e. satisfies some fixed + execution time constraints. Caller main specifies only + function name crucialOperation thus delegating decision on + guard to a polymorphism resolution done elsewhere, based on environment's + constraints the code is executed in. + + guard:: env(realtime) +{ + crucialOperation = function:: int + { 0 } +} + +main = function:: int; entry +{ + crucialOperation() +} +
+ +
+ Polymorphism Resolution + + SYNTAX: +**dfa_callguard**(//call-site-ref//, //guard//) + + + + call-site-ref reference to a call site in + AST + + + + guard resolved function specialization + guard + + + + When compiler encounters function invocation that has several + specialization it refers to the table dfa_callguard to find + out which specialization to call. It must have entry with appropriate + guard for every invocation site call-site-ref of + a polymorphic function. Polymorphism resolution is a process of filling + out dfa_callguard for a compiler based on custom Transcend + rules reflecting one or another polymorphism strategy. +
+ +
+ Late Polymorphism + + Late Polymorphism is an extension to allow polymorphism resolution + to be based on data known only at runtime, i.e. resolve function + specializations dynamically. The Idea is to use Late Transcend to access + runtime data. See Late + Transcend for details. + + Example below demonstrates test invoking polymorphic + function compute: + + Strategy = type variant {fast, precise}. + +guard:: fast +{ + compute = function:: int + {0} +} + +guard:: precise +{ + compute = function:: int + {1} +} + +test = function(s:: Strategy; alias(strategy)):: int; entry +{ + switch late (s):: int + { + compute():: int; guardalias(strategy) + } +} + + Function compute has two specializations, + fast and precise. We see that test + gets parameter s that dictates exact strategy to use. + Clearly, resolution should work dynamically to cope with cases like this, + for value of parameter s not only is unknown at compile time, + but it can change with each test execution. + + Operation Switch Late is compiled into several branches, two in this + case, each branch executing appropriate compute + specialization. Correct branch executed depending on current + s value. Custom annotations alias(Alias) and + guardalias(Alias) used to assign an alias in order to specify + which parameter to use for as basis for resolution +
+ +
+ Auto Expansion of Late Parameters + + In previous example, switch late operation was used to facilitate + calling of polymorphic function with late polymorphism resolution. It's + not that convenient to wrap each invocation by switch late whenever there + is need to call late polymorphic function. Specifically to handle cases + like this, compiler uses late parameter auto expansion technique. + + If compiler discovers that late(dfa_callguard()) entry + exists for current invocation and it does not have enclosing switch late + already, compiler automatically generates different branches that invoke + relevant specializations and transfers control to a branch depending on + late parameter value. In other words invocation implicitly wrapped into + switch late instruction if needed. +
+
diff --git a/documentation/Syntax/modules.xml b/documentation/Syntax/modules.xml index 0803122..cf61944 100644 --- a/documentation/Syntax/modules.xml +++ b/documentation/Syntax/modules.xml @@ -1,256 +1,262 @@ Modules Xreate offers modules as a way to organize and reuse source code. For simplicity, it's implemented as one file—one module. Modules often require prior compilation of other modules for correct work. It leads to a problem of a resolution where required module is located. Especially since modern software products usually have complicated and volatile file structure depending on exact configuration and platform to build. Common practice is to rely on build configuration tools to provide exact path for each module. For this reason Xreate interferes as little as possible with resolution. Language does not support for module to directly specify any path be it relative or absolute of other required modules. Also compiler does not search modules in some predefined list of directories and does not assume anything about project's file structure. It expects resolution information already fully defined before compilation. However compiler has optional built-in functionality to facilitate resolution. It is the very kind of problems the transcend level suited excellently for. It is modeled as supply and demand approach and lets modules to declare what they provide and what require expressed by annotations. Compiler then tries to satisfy requirements and find a match. Alternatively, external tools always can be used.
Module Headers - SYNTAX: - -module [:: //annotations-list// ] (Full form) + SYNTAX: +**module** [:: //annotations-list// ] (Full form) { //module-statement//... -} +} -module :: //annotations-list// . (Simplified form) + **module** :: //annotations-list// . (Simplified form) -module-statement ::= - | require ( //annotation// ). (1) - | discover ( //path// ). (2) - | controller (//path//). (3) + //module-statement// ::= + | **require** ( //annotation// ). (1) + | **discover** ( //path// ). (2) + | **controller** (//path//). (3) annotations-list List of annotations delimited by semicolon annotation Any valid transcend expression path Absolute or relative path to controller Xreate recognizes number of module management statements. Those statements should be located in specific section module {...} of a source code which is called module header. Module can have several headers. All headers gathered from a whole file are combined into one before actual processing. Modules processing happens before compilation. It means any data produced in course of compilation is inaccessible at this stage
Requesting Modules Statement require(..) expresses which modules are required for correct compilation. - module { - require(logger). + module { + require(logger). } In this example module in question requires some other module that provides feature called logger. There is no way to specify direct external module location. Instead, module expresses requirement in abstract form as propositional expression which is later used by resolution to find exact match. - module{require(stringslib).} + module{require(stringslib).} processString = function(a:: string):: string { - someStrFunc(a) + someStrFunc(a) } module{require(mathlib).} processNumber = function(a:: num):: num { - someMathFunc(a) + someMathFunc(a) } Example above demonstrates using several headers in one file. It's particularly useful if developer finds it convenient to put requirements near the actual code that uses it. This way it can be easily spotted when requirements are no more needed. After all, code locality improves readability.
Module Annotations Module can declare additional information for various uses. This is expressed by annotations located in the header. They are called module annotations. For instance, module annotations can be used by module resolution to find modules that satisfy requirements of others. - module:: status(obsolete). + module:: status(obsolete). The example shows module that declares its status. It can be used by resolution to choose most appropriate module out of number of candidates. One way to view annotations used by resolution is to treat them as something that module provides. There are no predefined module annotations and developer can put - arbitrary information there. + arbitrary information there.
Modules Resolution Modules resolution is a process to find exact modules locations that match requests. Compiler does not search modules in predefined directories and does not assume anything about project's file structure. In order to allow developer to determine it themselves the compiler refers to two transcend tables - SYNTAX: - modules_resolution(//request//, //module-resolved//). (1) - modules_resolution(//request//, //module-resolved//, //module-context//). (2) + SYNTAX: + **modules_resolution**(//request//, //module-resolved//). (1) + **modules_resolution**(//request//, //module-resolved//, //module-context//). (2) request annotation used in statement request(...) module-resolved Path or identifier of a module that matches request module-context Path or identifier of a module that requires other module These tables contain resolved modules for all possible requests. Form (1) contains requests that should always be resolved to the same module. Form (2) contains such requests for which resolution depends on - requesting module module-context. + requesting module module-context. - modules_resolution(numlib, "/path/to/numlib"). + modules_resolution(numlib, "/path/to/numlib"). modules_resolution(strings, "/path/to/ansi-lib", "moduleA"). modules_resolution(strings, "/path/to/utf8-lib", "moduleB"). For the example above the compiler would always resolve path to numerical lib(numlib) as "/path/to/numlib" (line 1). However strings library would be resolved as "/path/to/ansi-lib" if requested in moduleA(line 2) and as "/path/to/utf8-lib" if - requested in moduleB(line 3). + requested in moduleB(line 3). When compiler encounters module request it looks up table modules_resolution (first or second form) to find path of the requested module. Tables can be populated by any means be it either transcend reasoning or external tools. - There is no defined order or priority or fallback behavior while + There is no defined order or priority or fall back behavior while looking into tables. If the same request occurs in both tables they are considered to be ill-formed
Advanced Modules Resolution Xreate provide additional layer, optional helper to simplify modules management. It introduces two more built-in statements that can be used in module header: Discover statement and Controller Statement. Discover Statement has the form discover (path).It allows to specify directory where compiler would recursively search for all xreate files and extract module header annotations. It gathers info about all found source files. Controller Statement has the form controller (path) and specifies path to modules resolution controller. Controller is a file that contains transcend rules in order to process data gathered by discovery and populate resolution tables as its result of work. Example below shows 3 modules: - //First Module + //First Module module:: name(testA); provide(superService); status(needToTestEvenMore). //Second Module module:: name(testB); provide(superService); status(needToTest). //Third Module module { require(superService). - discover(“/modules/path/”). - controller(“/path/to/controller”). + discover("/modules/path/"). + controller("/path/to/controller"). } Two modules offer the same feature provide(superSerivce). Third module requires it and specifies directory where to look up for needed files. Controller's task is to populate resolution table if needed module is found and choose appropriate candidate, if more than one module offer this service. One way to decide what to choose in this example is to look at status of both modules. Let score be assigned to each possible status. Let's say 0 for status(needToTestEvenMore), 1 for status(needToTest). Then controller would proceed with the best scoring module, Second Module in this case.
+ +
+ See Also + + Transcend: Modules + API +
diff --git a/documentation/Syntax/syntax.xml b/documentation/Syntax/syntax.xml index 84567ba..e03c42a 100644 --- a/documentation/Syntax/syntax.xml +++ b/documentation/Syntax/syntax.xml @@ -1,639 +1,707 @@ Syntax Literals, Expressions, Basic Statements Annotations Intrinsics: query Identifiers, Code Blocks Branch Statements Interfaces: Extern-C Functions Loops Other: Transcend, Versions Types Variants There are number of principles Xreate syntax based on: Follows SSA form: each identifier is defined only once and no redefinitions allowed Order in which identifiers are defined does not influence computation order. Identifiers are computed in order based on dependencies between expressions. Order in which identifiers are defines reflects preferences and what is convenient for a developer.
Literals and expressions In Xreate expressions have a form: - SYNTAX: -//expression// [:: //type//; //annotations-list// ] + SYNTAX: +//expression// [:: //type//; //annotations-list// ] where annotation-list is a list of annotations delimited by semicolon. Expressions consist of literals and various operations as follows: Literals numbers, strings: 5, "Nimefurahi kukujua" Lists, records Record is a collection of elements of different types - {year = 1934, month = "april"}. List is a collection of elements of the same type without keys - {16, 8, 3} Arithmetic operations Basic arithmetic operations: +, -, *, / Relations ==, !=, <>, <, <=, >, >=. Both !=, <> mean not equal relation. Examples: 8>=3, "Blue" <> "Green" List and struct operations index operation to access individual elements of a list or a record. Example: colors = {"Green", "Blue"}::[string]. color = colors[0]:: string. Record's element access: date = {year = 1934, month = "april"}. year = date["year"] Identifiers Example: a - b Functions Example: result = isOdd(6).
Code Blocks Block is a list of expressions delimited by period. It has a body - main expression and optionally some identifier definitions. - SYNTAX: + SYNTAX: { [//ident// = //expression// . | //body-expression// . ].. -} +} - In order to compute code block the compiler search a body expression - - only one expression that does not have assignment in that block. Result - of body computation used as a result of block computation. If any - expression includes identifiers they are computed first. + Code block consists of expression called body + expression and optional set of assignments to define + identifiers used by body expression. Block computation is defined as + result of associated body expression computation. If any expression + encountered during computation includes some identifiers they are computed + first. - { + test = function:: int +{ a = 10:: int. b = 2:: int. a + b:: int } Above is an example of code block which have a+b as a body expression. In this case body depends on identifiers a, b so compiler computes both of them beforehand. Computation order depends only on dependencies between expressions. This approach has properties as follows: Mutually independent identifiers can be evaluated in any order Identifier gets computed only if it's required by block body expression or other required identifier
Functions - SYNTAX: -//function-name// = function ([//argument//:: //type//[; //annotation-list//]]...):: //return-type// [; //annotations//]... -//function-block// + SYNTAX: +//function-name// = **function** ([//argument//:: //type//[; //annotation-list//]]...):: //return-type// [; //annotations//]... +//function-block// function-name name of function argument formal parameter. Arguments delimited by comma. type, return-type formal parameter and returning value types function-block code block that holds function definition annotations list of annotations delimited by semicolon - Below is an example of a simple function sum. It has - two arguments and returns their sum. Also it has few annotations. First + Below is an example of a function sum. It takes two + arguments and returns their sum. Also it has few annotations. First annotation entry has a special meaning - it depicts entry point or main function in a program. Second annotation status(needs_review) is an example that developers can annotate function using custom annotations to express different properties. - sum = function(x:: int, y:: int):: int; entry; status(needs_review) + sum = function(x:: int, y:: int):: int; entry; status(needs_review) { x+y }
+
+ Function Specializations + + SYNTAX: +**guard**:: //annotation// +{ + //functions-list// +} + + + + annotation Guard expressed by + annotation + + + + functions-list one or more function that + share the same guard + + + + Xreate allows several functions called + specializations to share the same name. This is + syntactic foundation for function level polymorphism + i.e. ability for compiler to decide which exactly function is called out + of several options. Resolution can happen during compilation or at + run-time. + + Functions with the same name i.e. different specializations should + have unique identifiers called guards. When function + is actually called it's expected that resolution is already done at some + point before and supplies correct guard to uniquely specify which exactly + specialization to call. + + Example: + + guard:: safe_enviroment +{ + sum = function (a::int, b::int) :: int + { + a + b + } +} + + + See API to + get more information on how guards are processed + +
+
Branch Statements
IF Statement - SYNTAX: -if (//condition//):: //type// [; //annotations// ].. + SYNTAX: +**if** (//condition//):: //type// [; //annotations// ].. //block-true// - else - //block-false// + **else** + //block-false// IF statement executes block-true or block-false depending on condition evaluation result. Example: - answer = if (question == "Favorite color?"):: string + answer = if (question == "Favorite color?"):: string {"Yellow"} else {"Don't know"}.
SWITCH Statement - switch ( //condition// ) :: //type// [; //annotations//].. -[case ( //guard// ) code-block].. -case default //default-code-block// - + SYNTAX: +**switch** ( //condition// ) :: //type// [; //annotations//].. +[**case** ( //guard// ) code-block].. +**case default** //default-code-block// + condition's result is used to decide which branch to execute next guard value to match against condition default-code-block executed if no - appropriate cases found + appropriate case found SWITCH statement evaluation's result is that of branch whose guard matches condition. Example: - monthName = switch(monthNum) :: string + monthName = switch(monthNum) :: string case (1) {"Jan"} case (2) {"Feb"} case default {"Strange.. Don't know this month"}.
Loop Statements
LOOP Statement - SYNTAX: -loop ( //init-value// -> //accumulator// ) //loop-body// + SYNTAX: + +**loop** ( //init-value// -> //accumulator// ):: //type// [; //annotations//] //loop-body// init-value initial value loop starts from accumulator identifier which holds loop's result after each iteration For each iteration accumulator assumes result of previous iteration or init-value during first iteration. Result of the loop-body evaluation is used as accumulator's next iteration value and as overall loop statement result after the last iteration. This notation does not have termination condition. Compiler relies on loop body fixed point in order to decide when to interrupt loop. Let's consider example: - //infinite loop + COUNTEREXAMPLE +//infinite loop answer = loop (2->x) :: int { if(IsPerfect(x)):: int {x} else {x+1} }. The example tests numbers for being perfect(sum of all proper divisors equals to the number itself). While iterating accumulator x assumes values as follows: 2, 3, 4, 5, 6, 6 ... After it founds first perfect number any further iteration do not change result anymore since there is no increment and it continues to test the same number again and again. Obviously, x=6 is a fixed point in this example. There is no point to continue going through further iterations once fixed point is evaluated and hence loop can be safely interrupted. Compiler relies on manually provided annotations to recognize when fixed point is reached. There is special annotation final to specify fixed point for loops. Once expression marked as final gets evaluated it's assumed to be fixed point or in other words compiler knows it's the very last iteration after which loop ends. Correct code for the example above is: - //loop exits after first perfect number is found + //loop exits after first perfect number is found answer2 = loop (2->x) :: int { if(IsPerfect(x))::int {x:: int; final} else {x+1} }. In this case compiler able to recognize when fixed point is reached to exit loop. After loops is done answer is 6.
LOOP FOLD Statement - SYNTAX: -loop fold (//list// -> //element//:: //type// [; //annotations//], //init-value// -> //accumulator//):: //type// [; //annotations//] - //loop-body// + SYNTAX: + +**loop fold** (//list// **->** //element//:: //type// [; //annotations//], //init-value// **->** //accumulator//):: //type// [; //annotations//] + //loop-body// list to iterate through element identifier that assumes value of currently processed list element type, annotations expression types and optional annotations delimited by semicolon init-value accumulator's initial value loop starts with accumulator identifier assumes loop-body evaluation result after each iteration - Iterates over list and stores intermediate - result in accumulator. Overall loop value is a - accumulator's value after the last iteration. If fixed point is found - during execution terminates earlier. + Iterates over list in order to accumulate + result by applying loop-body transformation to each + element and intermediate + accumulator. Overall loop value is a accumulator's + value after the last iteration. If fixed point is found an execution + terminates earlier. + + Example shows code excerpt that looks for a minimal element in the + given list(and less then initial value 10). - numbers = {4, 8, 7, 1, 5}:: [int]. + numbers = {4, 8, 7, 1, 5}:: [int]. min = loop fold(numbers->x:: int, 10->acc):: int { if (acc > x):: int {x} else {acc} }. - - Example shows code excerpt that looks for a minimal element in the - given list(and less then initial value 10).
LOOP MAP Statement - SYNTAX: -loop map ( //list// -> //element// :: //type// [; //annotations// ] ) :: //type// [; //annotations// ] - //loop-body// + SYNTAX: + +**loop map** ( //list// **->** //element// :: //type// [; //annotations// ] ) :: //type// [; //annotations// ] + //loop-body// list to iterate through element identifier that assumes value of currently processed list element type, annotations type and optional annotations delimited by semicolon. loop-body - Iterates over list and creates another list with elements that are - results of a transformation by loop-body of - iterated elements of the given list. + Iterates over input list and applies + loop-body transformation to each element. Result is + a list that consists of all transformed elements. - odd_numbers = {1, 3, 5}:: [int]. + odd_numbers = {1, 3, 5}:: [int]. even_numbers = loop map(odd_numbers->number::int) :: [int] { 2 * number }. Example demonstrates creating even_number list by multiplying by 2 every element of odd_numbers.
Types Primitive Types num i32 alias. Reserved for auto detected most appropriate either integral of floating-point number type int i32 alias. Reserved for auto detected most appropriate integral number type float Double precision floating-point number bool Boolean type i8, i32, i64 Signed integers. 8, 32, 64 bit wide respectively string Pointer to a null terminated ANSI char string. Reserved for auto detected most appropriate string type. * Unspecified type. Example x = {amount=200, currency="USD"}::*. Compound types: [ element-type ] List of elements of the same type element-type. Example: [int] - list of int's {key:: type, ...} List of elements of different type possibly with named keys. Examples: {int, string}, {name::string, age::int} variant {option :: (type, ...}, ...} Holds a single element of type of one out of number of options. Examples: variant {FullAddress:: {string, string, string}, ShortAddress:: {string}} slave identifier Type determined by Transcend. Example: slave unit_test compound-type [ key ] Accessing elements of compound type. Example: Bio = type {birth_year:: int, name:: string}. Year = type Bio[birth_year]. New types defined as: - type-name = type (parameters...) type-definition . + SYNTAX: + +//type-name// = **type** (//parameters//...) //type-definition// . Example: - Tuple = type {string, int}. + Tuple = type {string, int}. Int = type Tuple[1]. //accessing by index
Variants and SWITCH VARIANT Instruction - SYNTAX: -switch variant ( condition [-> alias ] [:: type [; annotations... ] ] ) :: type [; annotations... ] -[ case ( guard ) case-branch ]... + SYNTAX: + +**switch variant** ( //condition// [-> //alias// ] [:: //type// [; //annotations//... ] ] ) :: type [; annotations... ] +[ **case** ( //guard// ) //case-branch// ]... condition expression of variant type alias identifier to denote unwrapped content of condition withing case branches. guard name of variant to match against actual condition's variant case-branch block of code to execute in case of matched variant. Content is accessible by using alias Within the branch . Sometimes it is useful for a variable to have value of different types depending on some conditions, in other words it has variant type. Let's consider example with variable month of variant type Month: - Month = type variant { - MonName:: {string}, + Month = type variant { + MonName :: {string}, MonNumber:: {int} }. -month = MonName("April"):: Month. +test = function:: Month +{ + month = MonName("April"):: Month. + month +} Variable month holds value of either string or int type. Value is not accessible directly. It should be unwrapped before using. Xreate supports switch variant instruction for this operation. As an example below is nextMonth function definition: nextMonth = funcrtion(month:: Month):: Month { switch variant(month):: Month case (MonName) { // } - case + case (MonNumber) }
diff --git a/documentation/Transcend/ast-api.xml b/documentation/Transcend/ast-api.xml new file mode 100644 index 0000000..75f4cb0 --- /dev/null +++ b/documentation/Transcend/ast-api.xml @@ -0,0 +1,450 @@ + + + AST API + + In order to reason about program, code model is translated into form + suited for processing by Transcend. Translation details are described + below. + +
+ Expression Annotations: 'bind' + + SYNTAX: +**bind**(//symbol-ref//, //annotation//) + + + + symbol-ref assigned reference to the + expression or identifier + + + + annotation expression's annotation + + + + Generated for each expression's annotation. Example: + + x = 5:: int; expected(even_number). + + gets translated into: + + bind(v(1,-2,0), expected(even_number)). +
+ +
+ Code Block: 'scope' + + SYNTAX: +**scope**(//scope-ref//) + + + + scope-ref reference to the code + block + + + + Declares code block under its unique reference identifier. + + Example: + + { + x = 0.3:: float. + y = 0.8:: float. + + x + y +} + + Translation result: scope(2). +
+ +
+ Code Block Annotations: 'bind_scope' + + SYNTAX: +**bind_scope**(//scope-ref//, //annotation//, strong) (1) +**bind_scope**(//scope-ref//, //annotation//, weak(..)) (2) + + + + + scope-ref child block's reference + + + + annotation code block's annotation + + + + Declares code block's annotations called + context. There are two forms for different context' + type: + + + + strong context known at compile time + + + + weak possible context, can't be decided for + sure at compile time + + + + Example: + + { + context:: arithmetic(fast). + + x = 0.3:: float. + y = 0.8:: float. + + x + y +} + + Translation's result: + + bind_scope(0, arithmetic(fast), strong). +
+ +
+ Code Block Bindings: 'ast_scope_binding' + + SYNTAX: +**ast_scope_binding**(//scope-ref//, //binding-id//, //binding-alias//) + + + + + scope-ref code block's reference + + + + binding-id number of code block's + binding + + + + binding-alias name of a binding + + + + Code blocks can have bindings, i.e. Identifiers + that have special meaning within a block. Declared for each block's + binding. + + Example: + + loop ( 0 -> acc ) :: int +{ + if(acc > 10) {acc:: int; final} else {acc + 1} +} + + Translation result: ast_scope_binding(2, 0, acc) +
+ +
+ Code Block Parents: 'cfa_parent' + + SYNTAX: +**cfa_parent**(//scope-ref//, scope(//scope-parent-ref//)) + + + + scope-ref child block's reference + + + + scope-parent-ref parent block's + reference + + + + Represents nested code blocks structure in terms of + child-parent relation. + + Example: + + { + if (x>5):: int {x + 1} else {x - 1} +} + + Translation's result: + + cfa_parent(1, scope(0)). +cfa_parent(2, scope(0)). +
+ +
+ Function: 'function' + + SYNTAX: +**function**(//fn-name//) + + + + fn-name function name + + + + Declares function known by its name. + + Example: + + test = function:: int {0} + + Translation's result: function(test) +
+ +
+ Function's Annotations: 'bind_func' + + SYNTAX: +**bind_func**(//fn-name//, //annotation//) + + + + fn-name function's name + + + + Declares function's annotations. + + Example: + + test = function:: int; status(obsolete) {0} + + Translation's result: + bind_func(test,status(obsolete)) +
+ +
+ Function's Specialization: 'cfa_function_specializations' + + SYNTAX: +**cfa_function_specializations**(//fn-name//, //guard//) + + + + fn-name name of function + + + + guard specialization guard + + + + There is a possibility to have several functions with the same name + called specializations. Each specialization is uniquely determined by + annotation of special kind called guard. + + Example: + + guard:: arch(amd64) +{ + test = function:: i64 {0} +} + + Translation's result: + cfa_function_specializations(test,arch(amd64)) + See also specializations + syntax + +
+ +
+ Function's Entry: 'cfa_parent' + + SYNTAX: +**cfa_parent**(//scope-entry-ref//, functon(//fn-name//)) + + + + scope-entry-ref function's entry code block + reference + + + + fn-name function's name + + + + Each function has a single entry code block and + is declared in terms of child-parent relation between + entry block(which is top-level in blocks hierarchy of the given function) + and the function itself. + + Example: + + test = function:: int {0} + + Translation's result: cfa_parent(0, + function(test)) +
+ +
+ Function's Result: 'dfa_fnret' + + SYNTAX: +**dfa_fnret**(//fn-name//, //symbol-ret-ref//) + + + + symbol-ret-ref reference to a function's return + expression + + + + Specifies which expression is used to compute function's return + value. + + Example: + + test = function:: int {0} + + Translation's result: dfa_fnret(test, v(0,-2,0)) +
+ +
+ Operations. Invocation: 'cfa_call', 'dfa_callfn', + 'dfa_callargs' + + SYNTAX: +**cfa_call**(//scope-caller-ref//, //fn-callee-name//) (1) +**dfa_callfn**(//symbol-ref//, //fn-callee-name//) (2) +**dfa_callargs**(//symbol-ref//, //arg-formal-name//, //arg-actual-ref//) (3) +**weak**(**dfa_callargs**(//symbol-ref//, //arg-formal-name//, //arg-actual-ref//)) (4) + + + + + scope-caller-ref caller's code block's + reference + + + + fn-callee-name callee function name + + + + symbol-ref invocation operation + reference + + + + arg-formal-name function's formal + argument + + + + arg-actual-ref actual argument + reference + + + + Each function invocation is transformed into several transcend facts + as explained below: + + + + + + + + + + (1) cfa_call + + Specifies caller's code block and callee function + name + + + + (2) dfa_callfn + + Declares unique reference to a particular invocation + site.The reference used by other facts to supply additional + information + + + + (3) dfa_callargs + + Declares assignment relations between actual arguments and + formal arguments + + + + (4) weak(dfa_callargs) + + The same as form (3). Weak relation + used in cases when there is no enough information at compile time. + One such case is when several function specializations exist and + it's impossible to say which exactly specialization is + called + + + + + + Example: + + inc = function (x::int) :: int {x + 1} + +main = function:: int +{ + arg = 10:: int + inc(arg) +} + + After translation following transcend facts are present: + + cfa_call(1, test) +dfa_callfn(v(0,1,-2), test) +dfa_callargs(v(0,1,-2), x, v(1,1,-2) +
+ +
+ Operations. Loops + + **ast_op_map**(//symbol-result-ref//, //symbol-source-ref//, //loop-body-ref//) +**ast_op_fold**(//symbol-result-ref//, //symbol-source-ref//, //symbol-acc-ref//, //loop-body-ref//) + + + + symbol-source-ref input list reference + + + + symbol-acc-ref accumulator reference + + + + symbol-result-ref result list reference + + + + loop-body-ref refers to a loop body's code block + + + + Facts declare loop operations. + + Example: + + singles = {1, 2, 3}:: [int]. +doubles = loop map(singles->element)::[int]{2 * element} + + produces fact: ast_op_map(v(0,-2,0),v(1,-2,0),1) +
+
diff --git a/documentation/Transcend/latetranscend.xml b/documentation/Transcend/latetranscend.xml new file mode 100644 index 0000000..e627b48 --- /dev/null +++ b/documentation/Transcend/latetranscend.xml @@ -0,0 +1,168 @@ + + + Late Transcend + + Transcend is a powerful tool and as a consequence it's computationally + inefficient. Well, expressiveness has its cost. That's the reason why it's + mostly suited only for a compile time where it shines unequivocally. + Obviously, at this stage compiler lacks dynamic part - the data that is + known for sure only during actual program execution. In other words, Early + Transcend reasons over weak data - set of facts + reflecting possible states and conditions the program could go through + during execution. Before that, there is no way to be sure what exactly is + going to happen, only set of possibilities is available. Nevertheless, in + many cases it's not enough and that's why Late + Transcend is introduced - reasoning and making decisions based on + data available at runtime. + + Late Transcend approach can be described as having three + phases: + + + + Input: at compile time Transcend working with weak data produces + decisions for all possible alternatives. Generated set is used as an + input for Late Transcend. As it's unknown which exactly decision turns + out to be true, every decision has a guards - set + of pairs (variable, value) that describe exact + state for which it's possible to reach given decision. + + + + Validation: A decision is considered to be valid only if all + guards are met, i.e. variables from guard pairs actually have required + values. In a sense Late Transcend decisions are deferred until + correspondent preconditions proved to be true. + + + + Execution: DIfferent guarded decisions may + produce different code branches. All correspondent code branches are + compiled and present in the code in order to be used when needed. At + branching point guards are tested and appropriate branch is selected to + be executed next. + + + +
+ Late Annotations and ASP Representation + + Late annotations is a particular type of + annotations that depend on parameters known only at runtime. + Example: + + y = mul(a, b)::float; arithmetic(ArithmX) + + Suppose we have different specializations of mul + function each implementing different multiplication precision and it + controlled by using arithmetic(fast), + arithmetic(accurate) and similar annotations. In this example + arithmetic(ArithmX) is a late annotation, if + ArithmX is a parameter known only during program + execution. + + Currently, late annotations do not differ syntactically from other + annotations. Only if annotation is recognized to have late arguments it's + marked internally as a late and special rules of + conversion to ASP form apply. + + Late annotation in ASP form presented as follows: + + **late**(//target//, (//guard-identifiers-list//), (//guard-values-list//), //body//):- //parameter-types-list//. + + + + target References entity for which late + annotation is applicable + + + + guard-identifiers-list List of guard + identifiers + + + + guard-values-list List of guard values + + + + + + body Decision deemed valid if guards are + met + + + + parameter-types-list List of types of late + parameters + + + + Meaning that a fact body wrapped by modifier + late() alongside with two lists: guards' identifiers and + guards' values. + + For an example above, arithmetic(ArithmX) translated + into form below where exact references depend on a program: + + late(s(0,-2,2), (s(1,-2,2)), (ArithmX), arithmetic(ArithmX)):- arithmetic(ArithmX). + + This rule generates different facts for each possible x + alternative. At runtime only those facts are selected whose guards are + met, i.e. referenced variables actually have specified values. +
+ +
+ Switch Late Operation + + SYNTAX: +**switch late** ( //condition// [-> //alias// ] [:: //condition-type// [; //annotations-list//] ] ) :: //type// [; //annotations-list//] + //switch-block// + + + + condition switch's condition + + + + alias optional alias to denote condition's + result within internal code block annotations. If there is no alias + and condition is just one identifier it's accessible by its own + name + + + + condition-type Condition must have slave type + + + + Switch Late operation allows to designate alias + as a late parameter to use in late annotation. It can + be conceptualised as lifting Brute value to Transcend level. During + compilation it generates different switch-block + branches for each possible condition value and only branch is executed + whose guard correspond condition. Example: + + ArithmeticT = type slave arithmetic. + +test = function(a::float, b::float, ArithmX::ArithmeticT):: float; entry +{ + switch late (ArithmX):: float + { + mul(a, b)::float; arithmetic(ArithmX) + } +} + + Compiler generates several branches for all possible + arithmetic variants. Only one branch executed depending on + arithmX. +
+
diff --git a/documentation/Transcend/latex-api.xml b/documentation/Transcend/latex-api.xml new file mode 100644 index 0000000..0644949 --- /dev/null +++ b/documentation/Transcend/latex-api.xml @@ -0,0 +1,102 @@ + + + Latex API + +
+ Latex Parameters: 'latex_registered_subjects' + + SYNTAX: +**latex_registered_subjects**(//parameter//, //decision//) + + + + parameter Name of particular latex + parameter + + + + decision Possible parameter's value + + + + Table to declare latex parameters along with all possible parameter + values - decisions. +
+ +
+ Block's Demand: 'latex_scope_demand' + + SYNTAX: +**latex_scope_demand**(//scope-ref//, //parameter//) + + Declares demand for a given scope specified by + scope-ref. It initiates latex reasoning in order to + find satisfying decision for each demand. +
+ +
+ Function's Demand: 'latex_fn_demand', + 'latex_fn_demand_ordered' + + SYNTAX: +**latex_fn_demand**(//fn-ref//, //parameter//) (1) +**latex_fn_demand_ordered**(//fn-ref//, //parameter//, //id//) (2) + + + + fn-ref function reference + + + + parameter latex parameter + + + + id number of the parameter in function + signature's arguments list + + + + Latex reasoning determines which functions require additional + parameters during runtime. Table (2) holds the same data as (1) along with + additional field id to specify exact order of latex + parameters in the given function's signature. +
+ +
+ Decisions: 'latex_decision', 'late(latex_decision)' + + SYNTAX: +**latex_decision**(//scope-ref//, //parameter//, //decision//); + + + + scope-ref code block reference + + + + parameter latex parameter + + + + decision latex decision + + + + Latex reasoning stores its output, found decisions, in this table. + Field decision specifies value of a latex parameter + parameter in a given code block denoted by + scope-ref. If some demand can't be satisfied and no + appropriate decision is found, compiler raises an error. Latex parameter + can have different values in different code blocks, thus + scope-ref is used as a guard. +
+
diff --git a/documentation/Transcend/modules-api.xml b/documentation/Transcend/modules-api.xml new file mode 100644 index 0000000..fb780af --- /dev/null +++ b/documentation/Transcend/modules-api.xml @@ -0,0 +1,116 @@ + + + Modules API + + Modules Resolution is a process of finding out which modules should be + preloaded for a correct compilation of a given module. + + Resolution expected to use bind_module, + modules_require as an input and produce results in form of + modules_resolution data table. + +
+ Module Annotations: 'bind_module' + + SYNTAX: +**bind_module**(//module-ref//, //annotation//) + + + + module-ref module's path + + + + Declares module annotations. + + Example: + + module:: status(obsolete). + + Produced fact: + bind_module(path/to/module, status(obsolete)) +
+ +
+ Requesting Modules: 'modules_require' + + SYNTAX: +**modules_require**(//module-ref//, //request//) + + + + module-ref module's path + + + + request expressed by annotation + + + + Module can request other modules necessary for correct work. + Declared requests can be processed to find a resolution. + + Example: + + module +{ + require(logger). +} + + Produced declaration: + modules_require(/path/to/module, logger) +
+ +
+ Modules Resolution: 'modules_resolution' + + SYNTAX: +**modules_resolution**(//request//, //module-resolved-ref//) (1) +**modules_resolution**(//request//, //module-resolved-ref//, //module-context-ref//) (2) + + + + + request expressed by annotation + + + + module-resolved-ref resolved module's + path + + + + module-context-ref context module's + path + + + + Xreate uses the data table to find correspondent modules for each + request. + + Form (1) assigns resolved module to a request and assumes that + assignment is valid in all cases, i.e. whenever compiler encounters + request it loads associated module. + + Form (2) assigns resolution valid only in a specific context. + Compiler uses particular resolution only for requests that came from a + module specified in a context. In other words, form (2) allows to resolve + the same request differently for different modules depending on resolution + strategy, thus implementing polymorphism on module + level. +
+ +
+ See Also + + Syntax: Modules +
+
diff --git a/documentation/Transcend/transcend.xml b/documentation/Transcend/transcend.xml new file mode 100644 index 0000000..743c93c --- /dev/null +++ b/documentation/Transcend/transcend.xml @@ -0,0 +1,337 @@ + + + Transcend + + Transcend is a compilation phase and process of reasoning about + program in order to influence compilation. + + First, compiler extracts annotations from source + code and other facts from number of sources to form a complete + logic program. Then, solver processes logic program and + outputs decisions that affect and control compilation process in a number of + ways. + +
+ Annotations' Syntax + + Xreate's annotations comprise optional supplementary information + that is processed by compiler along with a source code proper and able to + affect compilation process. Annotations have form of expressions that + consist of following elements: + + + + + + + + + + Literals + + Strings, numbers. Example: + 5, "success" + + + + Predicates + + Predicates have zero or more arguments. Example: + final, status(broken), version("12.0.3", unstable) + + + + Negation + + Example: -i12n, -access(allowed) + + + + + + Various Xreate's entities can be annotated. At the time following + entities are supported: + + + + + + Expressions + and Identifiers + + Code blocks and + context + + Functions and + Function + Guards + + Modules + + + + + +
+ Expression and Identifiers + + SYNTAX: +//expression// **::** //type//**;** //annotation-list//. + + + + type expression's type + + + + annotation-list one or more annotation + attached to the expression. Annotations are delimited by + semicolon + + + + Example: + + test = function:: int +{ + x = 5:: int; arithmetic(fast). //annotation applied to an identifier + y = (x - 8:: int; arithm_optimization(off)) * 2:: float. //annotation applied to a nested expression + + x+y +} + + + For readability sake compound statements(e.g. loops) have different + syntax for an annotations. See particular statement's syntax for details + +
+ +
+ Code Blocks and Context + + SYNTAX: +**context** :: //annotations-list//. + + + + annotations-list List of annotation + delimited by semicolon + + + + Code block annotations called context. + Keyword context used to declare annotation within enclosing + code block. + + Example: + + test = function:: int +{ + context:: arithm_optimization(off). + + x = 10 :: int. + 2 * x - 16 +} +
+ +
+ Special Annotations + + There are some annotations in Xreate that have special + meaning + + + + + + + + + + entry + + Specifies entry point of a program. See Function Syntax + + + + final + + Specifies fixed point of loop statements. See Loop + Statement + + + + +
+
+ +
+ Annotations and Reasoning + + Annotations is a mechanism to express an additional pieces of + information and can occur in source code or accompanying files. They are + of declarative nature in a sense that they express specific properties of + and relations between different entities, such as: identifiers, + statements, code blocks, functions, modules or even a whole + program. + + Annotations are facts about source code + provided by developer in explicit form. Beside annotations there are other + sources of information, e.g. implicit facts that are automatically + inferred by various analysis phases to express useful insights regarding + analysed code. Such pieces aren't exposed directly to a developer but + accessed and used internally at reasoning phase along with annotations. + All types of input are summed up below: + + + + + + + + + + Explicit annotations in source files + + Annotations provided by developer in order to explicitly + affect compilation + + + + Code Analysis + + Different code analyses during compilation provide + information implicitly inferred from source code + + + + Supervision + + External facts that reflect requirements, describe hardware + and/or environment where compilation takes place or program is + supposed to work. There are many supervision layers possible, from + custom compiler's mode, to OS- or computer- or even network-wide + level. On other words, supervision covers local or client supplied + annotations + + + + Audit + + Third party supplied external information that reflect + additional software properties, e.g. results of security + audit + + + + Reasoning rules + + Reasoning infers additional facts from pool of previously + gathered information. It is done by using resoning + rules that govern reasoning process + + + + +
+ +
+ Why Annotations Matter + + This section goes through differences that annotations have over + traditional statements. + +
+ Extensible + + Annotations are not a part of language syntax and there is no + predefined set of allowed annotations. Developer is free to define and + use custom annotations to express desired properties without prior + changes to compiler. + + + Yet there are several reserved annotations that have specific + meaning in Xreate. See special + annotations for details. + +
+ +
+ Indicative Nature + + Unlike language statements that have well defined compilation + output, annotation do not. They are rather of suggestive nature and + actual impact depends on many factors. Annotations comprise an input for + a reasoning procedure that ultimately determines how exactly influence + compilation. +
+ +
+ Cooperation + + Annotations are gathered from different sources and this enables + annotations to complement themselves. Compiler blends in annotations + originated from various sources seamlessly and this allows to improve + decisions quality. +
+ +
+ External Sources and Adaptability + + Annotations able to express not only properties of program itself + but also properties of local environment(OS, computer, local network) + where program is supposed to run. Thus compiler operates information + inaccessible during software development and can't be reflected in the + code otherwise. Compilation can be adjusted to a particular environment + and functioning mode. +
+ +
+ Feedback + + Traditionally software has a rigid strictly defined hierarchical + structure and interaction between components. More precisely, let's look + at client-service code model. Service is done independently in order to + work well with as wide as possible set of different clients. Thus + service is unaware of exact characteristics of particular client code it + works with. All burden of complying with interaction specification lies + on a client. + + However client may use only small subset of service functionality + or use it in specific and predictable order. Generally speaking, service + by having additional information on particular client able to serve + requests more efficiently in many cases. + + It can be conceptualized as a feedback from a + client. Annotations is good candidate to express feedback to improve + interaction quality. +
+ +
+ Resiliency + + As code grows and goes through multiple reworks and rewrites many + initial assumptions about the code and relation between different parts + change accordingly. Some implementation details and approaches became + outdated and code quality deteriorates. + + DIfferent picture in case if code uses annotations to express + important properties. Any changes in underlying code lead to a restart + of reasoning procedure which adjust implementation decisions to be still + optimal in changed environment. +
+
+
diff --git a/scripts/dsl/regexp.xreate b/scripts/dsl/regexp.xreate index 73d03f9..8c7b71b 100644 --- a/scripts/dsl/regexp.xreate +++ b/scripts/dsl/regexp.xreate @@ -1,76 +1,76 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ interface(extern-c){ xml2 = library:: pkgconfig("libxml-2.0"). include { xml2 = {"string.h"} }. } Matcher = type variant {Sequence, ZeroOrMore, Text}. matchText = function(text::string, matcher::string, posStart::i64):: i64 { textLength = strlen(text):: i64. matcherLength = strlen(matcher):: i64. if(textLength >= posStart + matcherLength):: i64{ if(strncmp(text + posStart, matcher, matcherLength) == 0):: i64 { matcherLength } else {-1:: i64} } else {-2:: i64} } matchSequence = function(text::string, pattern::undefType; i12n(on), posStart::i64):: i64; i12n(off){ textLength = length(text):: i64. loop fold(pattern-> matcher:: undefType, posStart->pos):: i64{ recognizedSymbols = match(text, matcher, pos):: i64. if (recognizedSymbols > (0::i64)):: i64{ pos+recognizedSymbols } else { - pos:: i64; break + pos:: i64; final } } } matchZeroOrMore= function(text::string, matcher::undefType; i12n(on), posStart::i64):: i64; i12n(off){ textLength = length(text):: i64. - loop fold inf(posStart->pos):: i64{ + loop (posStart->pos):: i64{ recognizedSymbols = match(text, matcher, pos):: i64. if (recognizedSymbols > (0::i64)):: i64{ pos+recognizedSymbols } else { - pos:: i64; break + pos:: i64; final } } } match = function(text::string, pattern::undefType; i12n(on), posStart::i64)::i64; i12n(off){ key= pattern[0]::Matcher. switch variant(key) :: int case (Sequence) {matchSequence(text, pattern[1], posStart)} case (ZeroOrMore) {matchZeroOrMore(text, pattern[1], posStart)} case (Text) {matchText(text, pattern[1], posStart)} } main = function:: i64; entry { patternAB = {Sequence(), {{ZeroOrMore(), {Text(), "a"}}, {Text(), "b"}}} :: undefType; i12n(on). // matchers = ["The ", "only ", "way "] :: [string]; i12n(on). match("aaab", patternAB, 0):: i64 }