diff --git a/config/default.json b/config/default.json index baf997f..a4c45db 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" + "onthefly": "onthefly" } }, "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": "troubleshooting", "templates": { - "troubleshooting":"Virtualization.Doc_*:Exploitation.Doc_*:Communication.Doc_*", - "documentation":"Modules.Doc_*:Interpretation.Doc_*:AST.Doc_*:Loop.Doc_*:LateReasoning.Doc_*:Latex.Doc_*:Polymorphs.Doc_*:Transcend.Doc_*:ASTCorrespondence.Doc_*:Virtualization.Doc_*:Exploitation.Doc_*:Communication.Doc_*", + "troubleshooting":"*.Doc_*:-Latex.*", + "documentation":"Modules.Doc_*:Modules_API.Doc_*:Interpretation.Doc_*:AST.Doc_*:Loop.Doc_*:LateReasoning.Doc_*:Latex.Doc_*:Polymorphs.Doc_*:Transcend.Doc_*:ASTCorrespondence.Doc_*:Virtualization.Doc_*:Exploitation.Doc_*:Communication.Doc_*:Introduction.*", "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.*" } } } diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp index a101e3b..7cf3994 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,771 +1,775 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * compilepass.cpp */ /** * \file compilepass.h * \brief Compilation pass */ #include "compilepass.h" #include "transcendlayer.h" #include #include "llvmlayer.h" #include "query/containers.h" #include "compilation/containers.h" #include "ExternLayer.h" #include "compilation/targetinterpretation.h" #include "pass/versionspass.h" #include "compilation/scopedecorators.h" #include "compilation/operators.h" #include "compilation/latex.h" #include "analysis/typeinference.h" #include #include #include using namespace std; using namespace llvm; namespace xreate{ namespace compilation{ std::string BasicFunctionUnit::prepareName() { AST* ast = IFunctionUnit::pass->man->root; string name = ast->getFunctionSpecializations(IFunctionUnit::function->__name).size() > 1 ? IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : IFunctionUnit::function->__name; return name; } std::vector BasicFunctionUnit::prepareSignature() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; std::vector signature; std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()), [llvm, ast, entry](const std::string & arg)->llvm::Type* { assert(entry->__identifiers.count(arg)); ScopedSymbol argid{entry->__identifiers.at(arg), versions::VERSION_NONE}; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type)); }); return signature; } llvm::Type* BasicFunctionUnit::prepareResult() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type)); } llvm::Function::arg_iterator BasicFunctionUnit::prepareBindings() { CodeScope* entry = IFunctionUnit::function->__entry; ICodeScopeUnit* entryCompilation = IFunctionUnit::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = IFunctionUnit::raw->arg_begin(); for (std::string &arg : entry->__bindings) { ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, argid); fargsI->setName(arg); ++fargsI; } return fargsI; } //DEBT compiler rigidly depends on exact definition of DefaultFunctionUnit typedef latex::LatexBruteFunctionDecorator< compilation::BasicFunctionUnit> BruteFunctionDefault; ICodeScopeUnit::ICodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : pass(compilePass), function(f), scope(codeScope), currentBlockRaw(nullptr) { } llvm::Value* BruteFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { llvm::Function* calleeInfo = dyn_cast(__callee); if (calleeInfo) { auto argsFormal = calleeInfo->args(); size_t sizeArgsF = std::distance(argsFormal.begin(), argsFormal.end()); assert(args.size() >= sizeArgsF); assert(calleeInfo->isVarArg() || args.size() == sizeArgsF); auto argFormal = argsFormal.begin(); for(size_t argId = 0; argId < args.size(); ++argId){ if(argFormal != argsFormal.end()){ args[argId] = typeinference::doAutomaticTypeConversion( args.at(argId), argFormal->getType(), llvm->builder); ++argFormal; } } } //Do not name function call that returns Void. std::string nameStatement = hintDecl; if (calleeInfo->getReturnType()->isVoidTy()) { nameStatement.clear(); } return llvm->builder.CreateCall(__calleeTy, __callee, args, nameStatement); } //DESABLEDFEATURE implement inlining class CallStatementInline : public IFnInvocation{ public: CallStatementInline(IFunctionUnit* caller, IFunctionUnit* callee, LLVMLayer* l) : __caller(caller), __callee(callee), llvm(l) { } llvm::Value* operator()(std::vector&& args, const std::string& hintDecl) { //TOTEST inlining // CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry); // for(int i=0, size = args.size(); ibindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i))); // } // // // return entryCompilation->compile(); return nullptr; } private: IFunctionUnit* __caller; IFunctionUnit* __callee; LLVMLayer* llvm; bool isInline() { // Symbol ret = Symbol{0, function->__entry}; // bool flagOnTheFly = SymbolAttachments::get(ret, false); //TODO consider inlining return false; } } ; BasicCodeScopeUnit::BasicCodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : ICodeScopeUnit(codeScope, f, compilePass) { } llvm::Value* BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { Expression declaration = CodeScope::getDefinition(s); const CodeScope* scopeExternal = s.scope; ICodeScopeUnit* scopeBruteExternal = ICodeScopeUnit::function->getScopeUnit(scopeExternal); assert(scopeBruteExternal->currentBlockRaw); llvm::Value* resultRaw; llvm::BasicBlock* blockOwn = pass->man->llvm->builder.GetInsertBlock(); if (scopeBruteExternal->currentBlockRaw == blockOwn) { resultRaw = scopeBruteExternal->process(declaration, hintRetVar); scopeBruteExternal->currentBlockRaw = currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); } else { pass->man->llvm->builder.SetInsertPoint(scopeBruteExternal->currentBlockRaw); resultRaw = scopeBruteExternal->processSymbol(s, hintRetVar); pass->man->llvm->builder.SetInsertPoint(blockOwn); } return resultRaw; } IFnInvocation* BasicCodeScopeUnit::findFunction(const Expression& opCall) { const std::string& calleeName = opCall.getValueString(); LLVMLayer* llvm = pass->man->llvm; const std::list& specializations = pass->man->root->getFunctionSpecializations(calleeName); //if no specializations registered - check external function if (specializations.size() == 0) { llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName); llvm::outs() << "Debug/External function: " << calleeName; external->getType()->print(llvm::outs(), true); llvm::outs() << "\n"; return new BruteFnInvocation(external, llvm); } //There should be only one specialization without any valid guards at this point return new BruteFnInvocation(pass->getFunctionUnit( pass->man->root->findFunction(calleeName))->compile(), llvm); } //DISABLEDFEATURE transformations // if (pass->transformations->isAcceptable(expr)){ // return pass->transformations->transform(expr, result, ctx); // } llvm::Value* BasicCodeScopeUnit::process(const Expression& expr, const std::string& hintVarDecl) { #define DEFAULT(x) (hintVarDecl.empty()? x: hintVarDecl) llvm::Value *left; llvm::Value *right; LLVMLayer& l = *pass->man->llvm; xreate::compilation::AdvancedInstructions instructions = xreate::compilation::AdvancedInstructions({this, function, pass}); switch (expr.op) { case Operator::SUB: case Operator::MUL: case Operator::DIV: case Operator::EQU: case Operator::LSS: case Operator::GTR: case Operator::NE: case Operator::LSE: case Operator::GTE: assert(expr.__state == Expression::COMPOUND); assert(expr.operands.size() == 2); left = process(expr.operands[0]); right = process(expr.operands[1]); break; default:; } switch (expr.op) { case Operator::ADD: { left = process(expr.operands[0]); Context context{this, function, pass}; llvm::Value* resultSU = StructUpdate::add(expr.operands[0], left, expr.operands[1], context, DEFAULT("tmp_add")); if (resultSU) return resultSU; right = process(expr.operands[1]); llvm::Value* resultAddPA = pointerarithmetic::PointerArithmetic::add(left, right, context, DEFAULT("tmp_add")); if (resultAddPA) { return resultAddPA; } return l.builder.CreateAdd(left, right, DEFAULT("tmp_add")); break; } case Operator::SUB: return l.builder.CreateSub(left, right, DEFAULT("tmp_sub")); break; case Operator::MUL: return l.builder.CreateMul(left, right, DEFAULT("tmp_mul")); break; case Operator::DIV: - return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div")); + if (left->getType()->isIntegerTy()) return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div")); + if (left->getType()->isFloatingPointTy()) return l.builder.CreateFDiv(left, right, DEFAULT("tmp_div")); break; case Operator::EQU: { if (left->getType()->isIntegerTy()) return l.builder.CreateICmpEQ(left, right, DEFAULT("tmp_equ")); if (left->getType()->isFloatingPointTy()) return l.builder.CreateFCmpOEQ(left, right, DEFAULT("tmp_equ")); const ExpandedType& leftT = pass->man->root->getType(expr.operands[0]); const ExpandedType& rightT = pass->man->root->getType(expr.operands[0]); if(leftT->__operator == TypeOperator::VARIANT && rightT->__operator == TypeOperator::VARIANT){ llvm::Type* selectorT = llvm::cast(left->getType())->getElementType(0); llvm::Value* leftUnwapped = typeinference::doAutomaticTypeConversion(left, selectorT, l.builder); llvm::Value* rightUnwapped = typeinference::doAutomaticTypeConversion(right, selectorT, l.builder); return l.builder.CreateICmpEQ(leftUnwapped, rightUnwapped, DEFAULT("tmp_equ")); } break; } case Operator::NE: return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne")); break; case Operator::LSS: return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss")); break; case Operator::LSE: return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse")); break; case Operator::GTR: return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr")); break; case Operator::GTE: return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte")); break; case Operator::NEG: left = process(expr.operands[0]); return l.builder.CreateNeg(left, DEFAULT("tmp_neg")); break; case Operator::CALL: { assert(expr.__state == Expression::COMPOUND); shared_ptr callee(findFunction(expr)); const std::string& nameCallee = expr.getValueString(); //prepare arguments std::vector args; args.reserve(expr.operands.size()); std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()), [this](const Expression & operand) { return process(operand); } ); return (*callee)(move(args), DEFAULT("res_" + nameCallee)); } case Operator::IF: { return instructions.compileIf(expr, DEFAULT("tmp_if")); } case Operator::SWITCH: { return instructions.compileSwitch(expr, DEFAULT("tmp_switch")); } case Operator::LOGIC_AND: { assert(expr.operands.size() == 1); return process(expr.operands[0]); } case Operator::LIST: { ExpandedType exprT = l.ast->getType(expr); bool flagIsArray; do { if (exprT->__operator == TypeOperator::CUSTOM){ if (l.layerExtern->isArrayType(exprT->__valueCustom)){ flagIsArray = true; break; } if (l.layerExtern->isRecordType(exprT->__valueCustom)){ flagIsArray = false; break; } assert(false && "Inapproriate external type"); } if (exprT->__operator != TypeOperator::LIST_ARRAY && exprT->__operator != TypeOperator::LIST_RECORD){ assert(false && "Inapproriate type"); } flagIsArray = exprT->__operator == TypeOperator::LIST_ARRAY; } while(false); if(flagIsArray){ return instructions.compileListAsSolidArray(expr, DEFAULT("tmp_list")); } const std::vector fieldsFormal = (exprT.get().__operator == TypeOperator::CUSTOM) ? l.layerExtern->getStructFields(l.layerExtern->lookupType(exprT.get().__valueCustom)) : exprT.get().fields; std::map indexFields; for (size_t i = 0, size = fieldsFormal.size(); i < size; ++i) { indexFields.emplace(fieldsFormal[i], i); } llvm::StructType* tyLiteralRaw = llvm::cast(l.toLLVMType(exprT)); llvm::Value* record = llvm::UndefValue::get(tyLiteralRaw); for (size_t i = 0; i < expr.operands.size(); ++i) { const Expression& operand = expr.operands.at(i); unsigned int fieldId = indexFields.at(expr.bindings.at(i)); llvm::Value* result = process(operand); assert(result); record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef({fieldId})); } return record; }; case Operator::LIST_RANGE: { assert(false); //no compilation phase for a range list // return InstructionList(this).compileConstantArray(expr, l, hintRetVar); }; case Operator::MAP: { assert(expr.blocks.size()); return instructions.compileMapSolidOutput(expr, DEFAULT("map")); }; case Operator::FOLD: { return instructions.compileFold(expr, DEFAULT("fold")); }; case Operator::INF: { return instructions.compileFoldInf(expr, DEFAULT("fold")); }; case Operator::INDEX: { //TASK allow multiindex compilation assert(expr.operands.size() == 2); assert(expr.operands[0].__state == Expression::IDENT); const std::string& hintIdent = expr.operands[0].getValueString(); Symbol s = Attachments::get(expr.operands[0]); const ExpandedType& t2 = pass->man->root->getType(expr.operands[0]); llvm::Value* aggr = processSymbol(s, hintIdent); switch (t2.get().__operator) { case TypeOperator::LIST_RECORD: case TypeOperator::CUSTOM: { std::string idxField; const Expression& idx = expr.operands.at(1); switch (idx.__state) { //named struct field case Expression::STRING: idxField = idx.getValueString(); break; //anonymous struct field case Expression::NUMBER: idxField = to_string((int) idx.getValueDouble()); break; default: assert(false && "Wrong index for a struct"); } return instructions.compileStructIndex(aggr, t2, idxField); }; case TypeOperator::LIST_ARRAY: { std::vector indexes; std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()), [this] (const Expression & op) { return process(op); } ); return instructions.compileArrayIndex(aggr, indexes, DEFAULT(string("el_") + hintIdent)); }; default: assert(false); } }; case Operator::CALL_INTRINSIC: { const std::string op = expr.getValueString(); if (op == "copy") { llvm::Value* result = process(expr.getOperands().at(0)); auto decoratorVersions = Decorators::getInterface(this); llvm::Value* storage = decoratorVersions->processIntrinsicInit(result->getType()); decoratorVersions->processIntrinsicCopy(result, storage); return l.builder.CreateLoad(storage, hintVarDecl); } assert(false && "undefined intrinsic"); } case Operator::QUERY: case Operator::QUERY_LATE: { assert(false && "Should be processed by interpretation"); } case Operator::VARIANT: { const ExpandedType& typVariant = pass->man->root->getType(expr); llvm::Type* typVariantRaw = l.toLLVMType(typVariant); llvm::Type* typIdRaw = llvm::cast(typVariantRaw)->getElementType(0); uint64_t id = expr.getValueDouble(); llvm::Value* variantRaw = llvm::UndefValue::get(typVariantRaw); variantRaw = l.builder.CreateInsertValue(variantRaw, llvm::ConstantInt::get(typIdRaw, id), llvm::ArrayRef({0})); const bool flagDoReference = expr.operands.size(); if (flagDoReference) { const ExpandedType& subtyp = ExpandedType(typVariant->__operands.at(id)); llvm::Type* subtypRaw = l.toLLVMType(subtyp); Attachments::put(expr.operands.at(0), subtyp); llvm::Value* subtypValue = process(expr.operands.at(0)); llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); llvm::Value* addrAsStorage = l.builder.CreateAlloca(typStorageRaw); llvm::Value* addrAsSubtyp = l.builder.CreateBitOrPointerCast(addrAsStorage, subtypRaw->getPointerTo()); l.builder.CreateStore(subtypValue, addrAsSubtyp); llvm::Value* storageRaw = l.builder.CreateLoad(typStorageRaw, addrAsStorage); variantRaw = l.builder.CreateInsertValue(variantRaw, storageRaw, llvm::ArrayRef({1})); } return variantRaw; } case Operator::SWITCH_VARIANT: { return instructions.compileSwitchVariant(expr, DEFAULT("tmpswitch")); } case Operator::SWITCH_LATE: { assert(false && "Instruction's compilation should've been redirected to interpretation"); return nullptr; } case Operator::SEQUENCE: { return instructions.compileSequence(expr); } case Operator::UNDEF: { llvm::Type* typExprUndef = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); return llvm::UndefValue::get(typExprUndef); } case Operator::INVALID: assert(expr.__state != Expression::COMPOUND); switch (expr.__state) { case Expression::IDENT: { Symbol s = Attachments::get(expr); return processSymbol(s, expr.getValueString()); } case Expression::NUMBER: { - llvm::Type* typConst = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); - int literal = expr.getValueDouble(); + llvm::Type* typConst = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); + int literal = expr.getValueDouble(); - return llvm::ConstantInt::get(typConst, literal); + if (typConst->isFloatingPointTy()) return llvm::ConstantFP::get(typConst, literal); + if (typConst->isIntegerTy()) return llvm::ConstantInt::get(typConst, literal); + + assert(false && "Can't compile literal"); } case Expression::STRING: { return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str")); }; default: { break; } }; break; default: break; } assert(false && "Can't compile expression"); return 0; } llvm::Value* BasicCodeScopeUnit::compile(const std::string& hintBlockDecl) { LLVMLayer* llvm = pass->man->llvm; if (!hintBlockDecl.empty()) { llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm->llvmContext, hintBlockDecl, function->raw); pass->man->llvm->builder.SetInsertPoint(block); } currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope}; return processSymbol(symbScope); } ICodeScopeUnit::~ICodeScopeUnit() { } IFunctionUnit::~IFunctionUnit() { } llvm::Function* IFunctionUnit::compile() { if (raw != nullptr) return raw; LLVMLayer* llvm = pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; string&& functionName = prepareName(); std::vector&& types = prepareSignature(); llvm::Type* expectedResultType = prepareResult(); llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false); raw = llvm::cast(llvm->module->getOrInsertFunction(functionName, ft)); prepareBindings(); const std::string&blockName = "entry"; llvm::BasicBlock* blockCurrent = builder.GetInsertBlock(); llvm::Value* result = getScopeUnit(function->__entry)->compile(blockName); assert(result); //SECTIONTAG types/convert function ret value builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->builder)); if (blockCurrent) { builder.SetInsertPoint(blockCurrent); } llvm->moveToGarbage(ft); return raw; } ICodeScopeUnit* IFunctionUnit::getScopeUnit(const CodeScope * const scope) { if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result) { return result.get(); } } std::shared_ptr unit(pass->buildCodeScopeUnit(scope, this)); if (scope->__parent != nullptr) { auto parentUnit = Decorators::getInterface(getScopeUnit(scope->__parent)); parentUnit->registerChildScope(unit); } else { __orphanedScopes.push_back(unit); } if (!__scopes.emplace(scope, unit).second) { __scopes[scope] = unit; } return unit.get(); } ICodeScopeUnit* IFunctionUnit::getScopeUnit(ManagedScpPtr scope) { return getScopeUnit(&*scope); } ICodeScopeUnit* IFunctionUnit::getEntry() { return getScopeUnit(function->getEntryScope()); } template<> compilation::IFunctionUnit* CompilePassCustomDecorators ::buildFunctionUnit(const ManagedFnPtr& function) { return new BruteFunctionDefault(function, this); } template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators ::buildCodeScopeUnit(const CodeScope * const scope, IFunctionUnit* function) { return new DefaultCodeScopeUnit(scope, function, this); } } // end of compilation compilation::IFunctionUnit* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { compilation::IFunctionUnit* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } void CompilePass::run() { //Initialization: managerTransformations = new xreate::compilation::TransformationsManager(); targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); //Determine entry function: StaticModel model = man->transcend->query(Config::get("function-entry")); assert(model.size() && "Error: No entry function found"); assert(model.size() == 1 && "Error: Ambiguous entry function"); string nameMain = std::get<0>(TranscendLayer::parse(model.begin()->second)); compilation::IFunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain)); //Compilation itself: entry = unitMain->compile(); } llvm::Function* CompilePass::getEntryFunction() { assert(entry); return entry; } void CompilePass::prepareQueries(TranscendLayer* transcend) { transcend->registerQuery(new containers::Query(), QueryId::ContainersQuery); transcend->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery); transcend->registerQuery(new latex::LatexQuery(), QueryId::LatexQuery); } } //end of namespace xreate /** * \class xreate::CompilePass * \brief Encapsulates all compilation activities * * xreate::CompilePass iterates over xreate::AST tree and produces executable code fed by data(via xreate::Attachments) gathered by previous passes as well as data via queries(xreate::IQuery) from xreate:TranscendLayer reasoner. * Compilation's done using xreate::LLVMLayer(wrapper over LLVM toolchain) and based on following aspects: * - Containers support. See \ref compilation/containers.h * - Late Conext compilation. See xreate::context::LateContextCompiler2 * - Interpretation support. See xreate::interpretation::TargetInterpretation * - Loop saturation support. See xreate::compilation::TransformerSaturation * - External Code access. See xreate::ExternLayer(wrapper over Clang library) * * \section adaptability_sect Adaptability * xreate::CompilePass's architecture provides adaptability by employing: * - %Function Decorators to alter function-level compilation. See xreate::compilation::IFunctionUnit * - Code Block Decorators to alter code block level compilation. See xreate::compilation::ICodeScopeUnit * Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit * - Targets to allow more versitile extensions. * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See xreate::compilation::Target * - %Altering Function invocation. xreate::compilation::ICallStatement * * Client able to construct compiler with desired decorators using xreate::compilation::CompilePassCustomDecorators. * As a handy alias, `CompilePassCustomDecorators` constructs default compiler * */ diff --git a/cpp/src/query/containers.cpp b/cpp/src/query/containers.cpp index 543fc00..a95fe9c 100644 --- a/cpp/src/query/containers.cpp +++ b/cpp/src/query/containers.cpp @@ -1,119 +1,116 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * containers.cpp * Created on 3/14/15. */ /** * \file query/containers.h * \brief Represents reasoner's solution on [Container implementations](/w/concepts/containers) */ #include #include "query/containers.h" using namespace std; using namespace xreate::containers; using namespace xreate; Implementation Query::queryImplementation(xreate::Symbol const &s) { - if (Attachments::exists(s)) - { - return Attachments::get(s); - } + if (Attachments::exists(s)) { + return Attachments::get(s); + } - return Implementation::create(s); + return Implementation::create(s); } - -Query::Query(){ - Attachments::init(); +Query::Query() { + Attachments::init(); } void -Query::init(TranscendLayer* transcend) -{ - if (flagDataIsLoaded) return; - - //Fill implementation data for a data sources: - auto range = transcend->query(Config::get("containers.id.implementations")); - if (range.size()) - for(auto atom: range) - { - auto data = TranscendLayer::parse(atom.second); - - Symbol var = transcend->unpack(get<0>(data)); - string implStr = get<1>(data).name().c_str(); - - if (implStr == Config::get("containers.impl.solid")) - { - auto size = TranscendLayer::parse(get<1>(data)); - Attachments::put(var, {SOLID, ImplementationRec{get<0>(size)}}); - - } else if (implStr == Config::get("containers.impl.onthefly")) { - Attachments::put(var, {ON_THE_FLY, ImplementationRec{var}}); - - } else { - assert(false && "Unable to determine proper implementation for the symbol"); - } +Query::init(TranscendLayer* transcend) { + if (flagDataIsLoaded) return; + + //Fill implementation data for a data sources: + auto range = transcend->query(Config::get("containers.id.implementations")); + if (range.size()) + for(auto atom : range) { + auto data = TranscendLayer::parse(atom.second); + + Symbol var = transcend->unpack(get<0>(data)); + string implStr = get<1>(data).name().c_str(); + + if (implStr == Config::get("containers.impl.solid")) { + auto size = TranscendLayer::parse(get<1>(data)); + Attachments::put(var,{SOLID, ImplementationRec + {get<0>(size)}}); + + } else if (implStr == Config::get("containers.impl.onthefly")) { + Attachments::put(var,{ON_THE_FLY, ImplementationRec + {var}}); + + } else { + assert(false && "Unable to determine proper implementation for the symbol"); + } } - flagDataIsLoaded = true; + flagDataIsLoaded = true; } Implementation -Implementation::create(const Symbol &var) -{ - //TODO review implementation determination strategy - Expression varDecl = CodeScope::getDefinition(var); - switch (varDecl.op) - { - case Operator::LIST_RANGE: { - ImplementationRec rec{var}; - return {ON_THE_FLY, rec}; - } - - case Operator::LIST: { - return {SOLID, ImplementationRec {varDecl.getOperands().size()}}; - } - - default: break; - }; - - ImplementationLinkedList ill(var); - if (ill){ - return ill.getImplementationData(); - } - - assert(false && "Unable to determine proper implementation for the symbol"); - return Implementation(); +Implementation::create(const Symbol &var) { + //TODO review implementation determination strategy + Expression varDecl = CodeScope::getDefinition(var); + switch (varDecl.op) { + case Operator::LIST_RANGE: + { + ImplementationRec rec{var}; + return {ON_THE_FLY, rec}; + } + + case Operator::LIST: + { + return {SOLID, ImplementationRec {varDecl.getOperands().size()}}; + } + + default: break; + }; + + ImplementationLinkedList ill(var); + if (ill) { + return ill.getImplementationData(); + } + + assert(false && "Unable to determine proper implementation for the symbol"); + return Implementation(); } ImplementationLinkedList::ImplementationLinkedList(const Symbol& source) - : flagIsValid(false), s(source){ +: flagIsValid(false), s(source) { - const Expression& sourceExpr = CodeScope::getDefinition(source); + const Expression& sourceExpr = CodeScope::getDefinition(source); - if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))){ - flagIsValid = true; + if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))) { + flagIsValid = true; - Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist")); - assert(tagLinkedlist.operands.size() == 2); - fieldPointer = tagLinkedlist.operands.at(0).getValueString(); - terminator = tagLinkedlist.operands.at(1); - } + Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist")); + assert(tagLinkedlist.operands.size() == 2); + fieldPointer = tagLinkedlist.operands.at(0).getValueString(); + terminator = tagLinkedlist.operands.at(1); + } } -ImplementationLinkedList:: operator bool () const{ - return flagIsValid; +ImplementationLinkedList::operator bool () const { + return flagIsValid; } Implementation ImplementationLinkedList::getImplementationData() const { - return {ON_THE_FLY, ImplementationRec{s}}; + return {ON_THE_FLY, ImplementationRec{s}}; } diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index a178bcc..8d6a2e4 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,58 +1,59 @@ 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 + introduction.cpp transcend-ast.cpp supplemental/docutils latetranscend.cpp cfa.cpp latex.cpp polymorph.cpp transcend.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/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/containers.cpp b/cpp/tests/containers.cpp index 8d0ba20..0e7482d 100644 --- a/cpp/tests/containers.cpp +++ b/cpp/tests/containers.cpp @@ -1,103 +1,155 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * containers.cpp * * Created on: Jun 9, 2015 * Author: pgess */ #include "xreatemanager.h" #include "query/containers.h" #include "main/Parser.h" +#include "supplemental/docutils.h" #include "gtest/gtest.h" using namespace std; using namespace xreate::grammar::main; using namespace xreate::containers; using namespace xreate; +typedef int (*IntFn)(); + TEST(Containers, ListAsArray){ XreateManager* man = XreateManager::prepare( R"Code( main = function(x:: int):: int;entry { a = {1, 2, 3}:: [int]. a[x] } )Code" ); void* mainPtr = man->run(); int (*main)(int) = (int (*)(int))mainPtr; ASSERT_EQ(2, main(1)); delete man; } TEST(Containers, ListAsArray2){ XreateManager* man = XreateManager::prepare( R"Code( // CONTAINERS import raw("scripts/dfa/ast-attachments.lp"). import raw("scripts/containers/containers.lp"). main = function:: int;entry { a= {1, 2, 3}:: [int]. b= loop map(a->el:: int):: [int]{ 2 * el }. sum = loop fold(b->el:: int, 0->acc):: int { acc + el }. sum } )Code"); void* mainPtr = man->run(); int (*main)() = (int (*)())mainPtr; ASSERT_EQ(12, main()); delete man; } TEST(Containers, ContanierLinkedList1){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); const Symbol symb_chilrenRaw{body->getSymbol("childrenRaw"), body}; containers::ImplementationLinkedList iLL(symb_chilrenRaw); ASSERT_EQ(true, static_cast(iLL)); ASSERT_EQ("next", iLL.fieldPointer); Implementation impl = Implementation::create(symb_chilrenRaw); ASSERT_NO_FATAL_FAILURE(impl.extract()); ImplementationRec recOnthefly = impl.extract(); ASSERT_EQ(symb_chilrenRaw, recOnthefly.source); } TEST(Containers, Implementation_LinkedListFull){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); std::unique_ptr program(XreateManager::prepare(input)); void* mainPtr = program->run(); int (*main)() = (int (*)())(intptr_t)mainPtr; int answer = main(); ASSERT_EQ(17, answer); fclose(input); } +TEST(Containers, Doc_Intr_1){ + string example = R"Code( + import raw("scripts/containers/containers.lp"). + + test = function:: int; entry + { + + x + } + )Code"; + string body = getDocumentationExampleById("documentation/Concepts/containers.xml", "Intr_1"); + replace(example, "", body); + + XreateManager* xreate = XreateManager::prepare(move(example)); + IntFn program = (IntFn) xreate->run(); + + int result = program(); + ASSERT_EQ(1, result); +} + +TEST(Containers, Doc_OpAccessSeq_1){ + string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessSeq_1"); + XreateManager* xreate = XreateManager::prepare(move(example)); + IntFn program = (IntFn) xreate->run(); + + int result = program(); + ASSERT_EQ(15, result); +} + +TEST(Containers, Doc_OpAccessRand_1){ + string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessRand_1"); + XreateManager* xreate = XreateManager::prepare(move(example)); + IntFn program = (IntFn) xreate->run(); + + int result = program(); + ASSERT_EQ(2, result); +} + +TEST(Containers, Doc_ASTAttach_1){ + string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "ASTAttach_1"); + string outputExpected = "containers_impl(s(1,-2,0),onthefly)"; + XreateManager* xreate = XreateManager::prepare(move(example)); + + testing::internal::CaptureStdout(); + xreate->run(); + std::string outputActual = testing::internal::GetCapturedStdout(); + + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} diff --git a/cpp/tests/introduction.cpp b/cpp/tests/introduction.cpp new file mode 100644 index 0000000..64f4d2b --- /dev/null +++ b/cpp/tests/introduction.cpp @@ -0,0 +1,25 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * Introduction.cpp + * + * Created on: March 2019 + * Author: pgess + */ + +#include "xreatemanager.h" +#include "transcendlayer.h" +#include "supplemental/docutils.h" +#include + +using namespace xreate; +using namespace std; + +TEST(Introduction, Doc_Example_1){ + string example = getDocumentationExampleById("documentation/index.xml", "Example_1"); + string rules = getDocumentationExampleById("documentation/index.xml", "Transcend_Example_1"); + std::unique_ptr man(XreateManager::prepare(move(example))); + man->transcend->addRawScript(move(rules)); + + int (* main)() = (int (*)()) man->run(); +} \ No newline at end of file diff --git a/cpp/tests/modules.cpp b/cpp/tests/modules.cpp index e9820aa..cc8e7c4 100644 --- a/cpp/tests/modules.cpp +++ b/cpp/tests/modules.cpp @@ -1,371 +1,404 @@ /* 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, 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 = getDocumentationExampleById("documentation/Syntax/modules.xml", "Requesting_Modules_1"); string codeController = R"Code( modules_resolution(logger, "/tmp/logger"). )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(1, result.size()); ASSERT_STREQ("/tmp/logger", result.front().c_str()); } TEST(Modules, Doc_Requesting_Modules_2){ 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 = getDocumentationExampleById("documentation/Syntax/modules.xml", "ModuleAnnotations_1"); Scanner scanner(reinterpret_cast(codeExcerpt.c_str()), codeExcerpt.size()); Parser parser(&scanner); parser.Parse(); ModulesSolver solver; solver.init("", parser.module); string program = solver.__program.str(); ASSERT_NE(string::npos, program.find("bind_module(\"\", status(obsolete)).")); } TEST(Modules, Doc_ModulesResolution_1){ 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); +} + +TEST(Modules_API, Doc_ModAnn_1){ + string program = getDocumentationExampleById("documentation/Transcend/modules-api.xml", "ModAnn_1"); + string outputExpected = getDocumentationExampleById("documentation/Transcend/modules-api.xml", "Output_ModAnn_1"); + replace(outputExpected, "/path/to/module", ""); + + auto xreate = new xreate::details::tier1::XreateManagerImpl>(); + testing::internal::CaptureStdout(); + + xreate->prepareCode(std::move(program)); + xreate->analyse(); + + std::string outputActual = testing::internal::GetCapturedStdout(); + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Modules_API, Doc_ReqMod_1){ + string program = getDocumentationExampleById("documentation/Transcend/modules-api.xml", "ReqMod_1"); + string outputExpected = getDocumentationExampleById("documentation/Transcend/modules-api.xml", "Output_ReqMod_1"); + replace(outputExpected, "/path/to/module", ""); + + Scanner scanner(reinterpret_cast(program.c_str()), program.size()); + Parser parser(&scanner); + parser.Parse(); + + ModulesSolver solver; + testing::internal::CaptureStdout(); + + solver.init("", parser.module); + + std::string outputActual = testing::internal::GetCapturedStdout(); + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); } \ No newline at end of file diff --git a/cpp/tests/supplemental/docutils.cpp b/cpp/tests/supplemental/docutils.cpp index 6fac2ad..a4f70f6 100644 --- a/cpp/tests/supplemental/docutils.cpp +++ b/cpp/tests/supplemental/docutils.cpp @@ -1,81 +1,95 @@ /* 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 #include using namespace std; std::string getDocumentationExampleById(std::string filename, std::string id) { string query = string("(//db:programlisting | //db:code | //db:screen)[@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 ""; } string tag = (char*) nodeset->nodeTab[0]->name; bool flagIsPrgListing = tag == "programlisting"; xmlChar* contentC = xmlNodeListGetString(doc, nodeset->nodeTab[0]->xmlChildrenNode, 1); string content = (char*) contentC; xmlFree(contentC); xmlXPathFreeObject(response); - if(flagIsPrgListing){ - //skip first line(used as header) - content = content.substr(content.find('\n')+1); + //skip first line(used as header) + if(flagIsPrgListing) { + content = content.substr(content.find('\n') + 1); } + + //replace no break spaces + replaceAll(content, "\u00A0", " "); + assert(content.size() && "Empty example"); return content; } std::string getDocumentationExampleFromFile(std::string filename) { std::ifstream stream(filename); std::string result; stream.seekg(0, std::ios::end); result.reserve(stream.tellg()); stream.seekg(0, std::ios::beg); result.assign((std::istreambuf_iterator(stream)), - std::istreambuf_iterator()); + std::istreambuf_iterator()); assert(result.size() && "Empty example"); return result; } void replace(std::string &str, const std::string &subA, const std::string &subB) { size_t posA = str.find(subA); assert(posA != string::npos); str.replace(posA, subA.size(), subB); +} + +void +replaceAll(std::string &str, const std::string &subA, const std::string &subB) { + size_t posA = str.find(subA); + + while(posA != string::npos) { + str.replace(posA, subA.size(), subB); + posA = str.find(subA); + } } \ No newline at end of file diff --git a/cpp/tests/supplemental/docutils.h b/cpp/tests/supplemental/docutils.h index 3f17d8f..0e47d69 100644 --- a/cpp/tests/supplemental/docutils.h +++ b/cpp/tests/supplemental/docutils.h @@ -1,24 +1,27 @@ /* 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); std::string getDocumentationExampleFromFile (std::string filename); void replace (std::string& str, const std::string& subA, const std::string& subB); +void replaceAll (std::string& str, + const std::string& subA, + const std::string& subB); #endif /* DOC_H */ diff --git a/documentation/Concepts/containers.xml b/documentation/Concepts/containers.xml new file mode 100644 index 0000000..c6e8384 --- /dev/null +++ b/documentation/Concepts/containers.xml @@ -0,0 +1,336 @@ + + + + + <?xxe-sn 2ahi4rjnvuo 2i?>Containers + +
+ + + <?xxe-sn 2alsjvonojk 1h?>Syntax + + Container operations: + + + + + + + + access(serial) + Denotes operation's type as a sequential access. More... + + + + + + access(rand) + Denotes operation's type as a random access. More... + + + + Container implementations: + + + + + + + + container(onthefly) + Lazy data structure that generates elements on the fly when needed. + More... + + + + + + container(solid) + Array or data structure that occupies contiguous memory region More... + + +
+ +
+ + + <?xxe-sn 2ahi4rjnvuo 2k?>Introduction + + Containers + is a general term referring to the data structures that contain a group of + elements of certain type. + + Considering that virtually every program + use containers to store, retrieve, search or otherwise process aggregate + data, obviously efficiency of containers implementation is a priority for + Xreate design. + + There are many different data structures + invented to serve as a containers, each of them having different + characteristics and peculiarities with no obvious winner but rather each + one suits the best in appropriate situations. Usually it is software + developer's knowledge and responsibility to be able to select the most + appropriate container's implementation for a particular use case. That + said, Xreate goes further and gathers information on how + containers are used, by analysing the program sources. On this + ground it's possible to choose semi-automatically the most appropriate + data structure for container implementation to efficiently fulfil + particular needs in a particular situation. + + In order to do this, the following approach + is used. The way a container is defined is associated with one or more + possible implementations it supports. On the other side, operations over + container demand certain "good" implementations to efficiently process + container data. Viewing it as supply + and demand setting, with a list of several acceptable + implementations from either side, the most appropriate tradeoff is chosen + as implementation for a given container to fulfill both sides with regard + to defaults, preferences, constraints and other ways to guide inference + process. + + In short example below + + name = "tests/containers.cpp: Containers.Doc_Intr_1" +//container definition: +a= {1, 2, 3, 4, 5}:: [num]; container(solid). + + +//container's operation +x= a[0]:: num; access(rand). + + container's offer + container(solid) and operation's + demand access(rand) + are explicitly expressed by annotations for clarity purposes. + + Annotation container(solid) + depicts that container a supports + implementation solid, that is plain + contiguous memory region or array. On the other side, annotation + access(rand) expresses nature of + retrieving by index + operation (variable x) and it + requires selected data structure to support random + access to be efficiently executed. + + Obviously, implementation solid + allows efficient random access and so it's assumed as a container + a's implementation by inference + process. + + Semi-automatic, guided container's + implementation selection has some advantages, such as: + + + + + + + + Less + manual work. Inferring adequate implementations with little + to no additional input from developer saves time to concentrate on + more important aspects. Other approach to achieve the same, namely to + assign default one-size-fits-all implementation with an average + performance, is simpler but can not compete with more careful + strategy. + + + + + + Rapid + development and optimization antagonism. It is important to + keep in mind, that rapid development and frequent code changes + somewhat contradict optimization efforts. Each round of optimization + is leveraged by relying on concrete and particular program properties, + overt or hidden connections and other observations. Once program + undergo further development most of the previously sound optimization + techniques became obsolete, irrelevant or plainly wrong. Selecting(as + often as needed) the most efficient data structures keeps reasonable + efficiency level automatically and does not impede possibly fast + development pace. + + + + + + Regression + resistance. Xreate encourages frequent changes, adjusting + and recombination in software components, libraries and modules by + automatically reevaluating and reassigning most appropriate data + structures in the new conditions or signalling error if it's + impossible. This somewhat alleviates problem of fragile + software and gives more confidence for refactoring. + + +
+ +
+ + + <?xxe-sn 2ahi4rjnvuo 33?>Container Implementations + + Xreate supports container implementations + presented below: + +
+ + + <?xxe-sn 2ahi4rjnvuo 5h?>Implementation + 'container(onthefly)' + + Source: range list operator + [from .. to]. + + Supported operations: access(serial). + + This is elementary implementation that + represents lazy data + structure — sequence of elements is generated by recurrence equation + applied to a current element to compute next element of the sequence. It + does not keep actual data in the memory, but instead computes necessary + elements when accessed. This kind of implementation is rather memory + efficient since occupied memory does not depend on count of + elements. + + For example, range list [1..10] + supports onthefly implementation + by using internally recurrent function x[i+1] = x[i] + 1, 1<= x <= 10, + that generates successive element x[i+1] + given x[i]. + + Recurrent elements generation is suited + for sequential access and can't serve random access operations. +
+ +
+ + + <?xxe-sn 2ahi4rjnvuo 5r?>Implementation + 'container(solid)' + + Source: list operator. + + Supported operations: access(serial), + access(rand). + + This is implementation from the opposite + side of the memory/computation space. It stores all the container's data + in memory occupying contiguous region, known as array. + As opposed to the implementation onthefly, + it's computationally efficient for there is no need for any additional + computations apart from simple offset calculation to get an element + requested by an index. + + Due to the fact that all elements are + present in the memory, the implementation supports sequential access as + well as random access operations. +
+
+ +
+ + + <?xxe-sn 2ahi4rjnvuo 3g?>Container Operations + + In order to describe requirements for a + container all the operations are broken down into several categories as + presented below. + +
+ + + <?xxe-sn 2alsjvonojk 2r?>Operation 'access(serial)' + + Operators: loop map, + loop fold. + + Annotation denotes sequential access + operation, such as map loop or + map fold. + + Example: + + name = "tests/containers.cpp: Containers.Doc_OpAccessSeq_1" +import raw("scripts/containers/containers.lp"). + +test = function :: int; entry +{ + range = [1..5] :: [int]; container(onthefly). + sum = loop fold(range->el:: int, 0->acc):: [int]; access(serial) + { + acc + el + }. + + sum +} +
+ +
+ + + <?xxe-sn 2alsjvonojk 2u?>Operation 'access(rand)' + + Operators: index + + Annotation denotes random access + operation. + + Example: + + name = "tests/containers.cpp: Containers.Doc_OpAccessRand_1" +import raw("scripts/containers/containers.lp"). + +test = function:: num; entry +{ + a = {1, 2, 3, 4, 5}:: [num]; container(solid). + a[1]:: num; access(rand) +} +
+
+ +
+ + + <?xxe-sn 2apiasqubk0 6?>AST Attachments + + In order to bypass tedious writing of + necessary annotations for each line of code that works with containers + there are appropriate annotations already defined for common operations. + All it takes for a client's code is to include transcend script + scripts/dfa/ast-attachments.lp that + allows to assign predefined annotations for syntactic constructs and + operators. + + Example below includes ast-attachments.lp + that feeds compiler with default annotations sparing developer of + specifying them manually. + + name = "tests/containers.cpp: Containers.Doc_ASTAttach_1" +import raw("scripts/containers/containers.lp"). +import raw("scripts/dfa/ast-attachments.lp"). + +test = function :: int; entry +{ + range = [1..5] :: [int]. + sum = loop fold(range->el:: int, 0->acc):: [int] + { + acc + el + }. + + sum +} +
+
diff --git a/documentation/Concepts/context.xml b/documentation/Concepts/context.xml index 3762cb8..020b673 100644 --- a/documentation/Concepts/context.xml +++ b/documentation/Concepts/context.xml @@ -1,365 +1,304 @@ <?xxe-sn 2ahi4rjnvuo 2?>Context - Computer program and its internal states and + Computer program, 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. + control flow and data flow. In order to express and employ control flow + related program's properties Xreate supports mechanism called + context — ability to assign + transcend data to a code blocks in a way that related blocks in the CFG have + related contexts. - Data that can be captured from analysing - control flow consists of two parts as follows: + Code block's context is exhaustingly defined + 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 + Block's place within the code in terms of + hierarchy of all its respective parents. - Historical data determined by all previous - visited code blocks + Historical data determined by previous + visited code blocks in terms of "caller-callee" relation graph. - Xreate allows to express Context and reason - about it by employing block level annotations. See Block level annotations are used to define + context and reason about it. See syntax.
<?xxe-sn 2ahi4rjnvuo e?>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. + In this example the annotation + env(utf8) conveys some information + about the block thus distinguishing it from the others, which allows 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. In this + case context can be viewed as a suggestion + to pick up appropriate specialization. - Different story with the next example. Here - we want to stipulate context properties: + Also there is possibility to define expected + context properties: name - "...." 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 + the example. If the 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. + appropriate specialization and reports compilation error. This is a way + for function to express requirements + or contract to a context it works within.
<?xxe-sn 2ahi4rjnvuo r?>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 contexts of different blocks are + connected reflecting control flow. Context of a particular code block is + gathered from following sources: - Context propagation means that nested blocks - inherit context of parents. - Moreover callee function's context inherits caller's one. Example: + + + + + + + Local context — annotations that are + defined within the block + + + + + + Parent context. Block's context inherits + context of its parent reflecting block's place within a + program. + + + + + + Client context. Blocks context also + includes caller's context reflecting control flow. It allows taking + into account in which context a given code is used. + + + + Example below name = "..." //requires 'safe' context -guard:: safe +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. + demonstrates context propagation to a nested + block which is exhibited by availability of a env(safe) + annotation in the context of the nested block blockB + despite being defined in the 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: + Another case is propagation from caller to + a callee. It means context propagates through "caller/callee" relation — + callee inherits caller context. The example below demonstrates + this: name = "..." toSI = function(x:: int):: int { 0 } calculate = function(x:: int):: int { y = toSI(x):: int. y } test = function:: int; entry { - context:: units(millimeters). + context:: units(mm). 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: - - name = "..." -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. + unit we can register it in a context, units(mm)in + this example, letting functions calculate() + and its callees inherit context allowing compiler to generate code + tailored for specific units only.
<?xxe-sn 2ahi4rjnvuo 1n?>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. + possible paths leading in a CFG to a given block. Beyond that are entries + that depend on exact path in CFG. Such uncertainty is possible to resolve + during runtime once it's known which path exactly is chosen. - However, for any cases it's crucial to - consider //possible contexts// that is, contexts valid only under certain - conditions. + To solve this problem late + context is introduced - embedding into compiled code certain + instructions to gathered data on relevant occasion at runtime to determine + exact or strong, + context. Latex approach can be described as follows: - All possible alternative contexts for - the given scope computed during compile time used as input for - Latex + Set of all possible context facts for + the given block is computed during compile time. Each such fact is + associated with code paths it holds for. - + - All possible paths are numerated and - specific latex parameter created to keep data about current - path. + During execution the information is + gathered which facts are hold depending on actual code path. + Information stored in the late parameters. To convey late context data + the latex parameter is injected into function signature as hidden + parameter. - Late parameter used as guard for Late - Transcend facts context consists of. + Late parameter is 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. - name = "..." 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 name = "..." 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) - -
- -
- - - <?xxe-sn 2ahi4rjnvuo 2a?>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. + To sum it up, context consists of two + complements parts: on the one hand static(early) + context, that denotes compile time inference's result and on + the other hand, late(dynamic) + context denotes annotations decided upon at runtime.
diff --git a/documentation/Concepts/polymorphism.xml b/documentation/Concepts/polymorphism.xml index c453c27..e757c58 100644 --- a/documentation/Concepts/polymorphism.xml +++ b/documentation/Concepts/polymorphism.xml @@ -1,224 +1,229 @@ <?xxe-sn 26n42fz1reo 2?>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
<?xxe-sn 26n42fz1reo j?>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) + name="tests/polymorph.cpp: Polymorphs.Doc_FnLvlPoly_1" +guard:: env(realtime) { crucialOperation = function:: int { 0 } } main = function:: int; entry { crucialOperation() }
<?xxe-sn 26n42fz1reo x?>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.
<?xxe-sn 26n42fz1reo 1c?>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}. + name="tests/polymorph.cpp: Polymorphs.Doc_LatePoly_1" +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
<?xxe-sn 26n42fz1reo 1x?>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.
\ No newline at end of file diff --git a/documentation/Syntax/modules.xml b/documentation/Syntax/modules.xml index c08f132..649dfa8 100644 --- a/documentation/Syntax/modules.xml +++ b/documentation/Syntax/modules.xml @@ -1,325 +1,335 @@ <?xxe-sn 26n42fz1reo 22?>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.
<?xxe-sn 26n42fz1reo 2d?>Module Headers SYNTAX: **module** [:: //annotations-list// ] (Full form) { //module-statement//... } **module** :: //annotations-list// . (Simplified form) //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
<?xxe-sn 26n42fz1reo 2x?>Requesting Modules Statement require(..) expresses which modules are required for correct compilation. - module { + name="tests/modules.cpp: Modules.Doc_Requesting_Modules_1" +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).} + name="tests/modules.cpp: Modules.Doc_Requesting_Modules_2" +module{require(stringslib).} processString = function(a:: string):: string { someStrFunc(a) } module{require(mathlib).} processNumber = function(a:: num):: num { 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.
<?xxe-sn 26n42fz1reo 37?>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). + name="tests/modules.cpp: Modules.Doc_ModuleAnnotations_1" +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.
<?xxe-sn 26n42fz1reo 3f?>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) 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. - modules_resolution(numlib, "/path/to/numlib"). - modules_resolution(strings, "/path/to/ansi-lib", "moduleA"). - modules_resolution(strings, "/path/to/utf8-lib", "moduleB"). + name="tests/modules.cpp: Modules.Doc_ModulesResolution_1" +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). 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 fall back behavior while looking into tables. If the same request occurs in both tables they are considered to be ill-formed
<?xxe-sn 26n42fz1reo 46?>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 + name="tests/modules.cpp: Modules.Doc_AdvModRes_1" + +//First Module module:: - name(testA); - provide(superService); - status(needToTestEvenMore). + name(testA); + provide(superService); + status(needToTestEvenMore). //Second Module module:: - name(testB); - provide(superService); - status(needToTest). + name(testB); + provide(superService); + status(needToTest). //Third Module module { - require(superService). - discover("/modules/path/"). - controller("/path/to/controller"). + require(superService). + 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.
<?xxe-sn 26n42fz1reo 4w?>See Also Transcend: Modules API
\ No newline at end of file diff --git a/documentation/Syntax/syntax.xml b/documentation/Syntax/syntax.xml index 95f2184..209abbb 100644 --- a/documentation/Syntax/syntax.xml +++ b/documentation/Syntax/syntax.xml @@ -1,956 +1,959 @@ <?xxe-sn 26yv439af40 5a?>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 personal preferences and what is convenient for a developer.
<?xxe-sn 26yv439af40 62?>Literals and expressions Xreate expressions have a form: SYNTAX: //expression// [:: //type//; //annotations-list// ] 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, + of different types — {year = 1934, month = "april"}. List is a collection of elements of the - same type without keys - {16, 8, - 3} + same type without keys — {16, 8, + 3}. Range of elements — [1..18]. 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).
<?xxe-sn 26yv439af40 7c?>Code Blocks Block is a list of expressions delimited by period. It has a body - main expression and optionally some identifier definitions. SYNTAX: { [//ident// = //expression// . | //body-expression// . ].. } Code block consists of body expression and optional set of assignments to define identifiers used in body expression. Block's computation is defined as a result of associated body expression's computation. Identifiers are computed before expressions they are used in. name="tests/ast.cpp: AST.Doc_CodeBlocks1" 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(even transitively) by block's body expression.
<?xxe-sn 26yv439af40 7u?>Functions SYNTAX: //function-name// = **function** ([//argument//:: //type//[; //annotation-list//]]...):: //return-type// [; //annotations//]... //function-block// function-name name of function argument formal parameter. Arguments are delimited by comma. type, return-type formal parameter and returning value types function-block code block that acts as a function's definition annotations list of annotations delimited by semicolon Below is an example of a function sum. It takes two arguments and returns their sum. Also it defines several annotations. First annotation entry has a special meaning — it depicts entry point or main function in a program. Second annotation status(needs_review) is a demonstration that developers can annotate function using custom annotations to express different properties. name="tests/ast.cpp: AST.Doc_Functions1" sum = function(x:: int, y:: int):: int; entry; status(needs_review) { x+y }
<?xxe-sn 26yv439af40 8j?>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 to share the same name. In which case they are called specializations. This is syntactic foundation for function level polymorphism, i.e. ability for compiler to decide which exactly function is called out of several options. Polymorphism resolution can happen during compilation or at run-time. Functions with the same name, i.e. different specializations should have additional 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: name="tests/ast.cpp: AST.Doc_FunctionSpecializations1" guard:: safe_enviroment { sum = function (a::int, b::int) :: int { a + b } } See API to get more information on how guards are processed
<?xxe-sn 26yv439af40 93?>Branch Statements
<?xxe-sn 26yv439af40 95?>IF Statement SYNTAX: **if** (//condition//):: //type// [; //annotations// ].. //block-true// **else** //block-false// IF statement executes block-true or block-false depending on condition evaluation result. Example: name="tests/ast.cpp: AST.Doc_BranchStatements" answer = if (question == "Favorite color?"):: string {"Yellow"} else {"Don't know"}.
<?xxe-sn 26yv439af40 9e?>SWITCH Statement 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 case found SWITCH statement evaluation's result is that of branch whose guard matches condition. Example: name="tests/ast.cpp: AST.Doc_BranchStatements" monthName = switch(monthNum) :: string case (1) {"Jan"} case (2) {"Feb"} case default {"Strange.. Don't know this month"}.
<?xxe-sn 26yv439af40 9x?>Loop Statements
<?xxe-sn 26yv439af40 9z?>LOOP Statement 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's fixed point in order to decide when to interrupt loop. Let's consider example: COUNTEREXAMPLE, name="tests/ast.cpp: AST.Doc_LoopStatements" //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 a 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: name="tests/ast.cpp: AST.Doc_LoopStatements" //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.
<?xxe-sn 26yv439af40 aq?>LOOP FOLD Statement 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 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). name="tests/ast.cpp: AST.Doc_LoopStatements" numbers = {4, 8, 7, 1, 5}:: [int]. min = loop fold(numbers->x:: int, 10->acc):: int { if (acc > x):: int {x} else {acc} }.
<?xxe-sn 26yv439af40 bi?>LOOP MAP Statement 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 input list and applies loop-body transformation to each element. Result is a list that consists of all transformed elements. name="tests/ast.cpp: AST.Doc_LoopStatements" 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.
<?xxe-sn 26yv439af40 c6?>Types Primitive Types num i32 - alias. Reserved for auto detected most appropriate either integral - of floating-point number type + alias. Reserved as a placeholder for auto detected appropriate + either integral of floating-point type int i32 - alias. Reserved for auto detected most appropriate integral number - type + alias. Reserved as a placeholder for auto detected appropriate + integral 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: SYNTAX: //type-name// = **type** (//parameters//...) //type-definition// . Example: name="tests/ast.cpp: AST.Doc_Types" Tuple = type {string, int}. Int = type Tuple[1]. //accessing by index
<?xxe-sn 26yv439af40 ej?>Variants and SWITCH VARIANT Instruction 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: name="tests/ast.cpp: AST.Doc_Variants" Month = type variant { MonName :: {string}, MonNumber:: {int} }. 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 function nextMonth's definition: nextMonth = function(month:: Month):: Month { switch variant(month):: Month case (MonName) { // } case (MonNumber) }
\ No newline at end of file diff --git a/documentation/Transcend/modules-api.xml b/documentation/Transcend/modules-api.xml index fb780af..dc2d68e 100644 --- a/documentation/Transcend/modules-api.xml +++ b/documentation/Transcend/modules-api.xml @@ -1,116 +1,156 @@ - Modules API + - Modules Resolution is a process of finding out which modules should be - preloaded for a correct compilation of a given module. + <?xxe-sn 2ahy8s1pw5c 7g?>Modules API - Resolution expected to use bind_module, - modules_require as an input and produce results in form of - modules_resolution data table. + 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' + + + <?xxe-sn 2ahy8s1pw5c 7n?>Module Annotations: 'bind_module' - SYNTAX: + SYNTAX: **bind_module**(//module-ref//, //annotation//) + + - module-ref module's path + + + module-ref + module's path - Declares module annotations. + Declares module annotations. - Example: + Example: - module:: status(obsolete). + name="tests/modules.cpp: Modules_API.Doc_ModAnn_1" +module:: status(obsolete). - Produced fact: - bind_module(path/to/module, status(obsolete)) + Produced fact: bind_module("/path/to/module",status(obsolete))
- Requesting Modules: 'modules_require' + - SYNTAX: + <?xxe-sn 2ahy8s1pw5c 7z?>Requesting Modules: + 'modules_require' + + SYNTAX: **modules_require**(//module-ref//, //request//) + + - module-ref module's path + + + module-ref + module's path - request expressed by annotation + + + request + expressed by annotation - Module can request other modules necessary for correct work. - Declared requests can be processed to find a resolution. + Module can request other modules necessary + for correct work. Declared requests can be processed to find a + resolution. - Example: + Example: - module + name="tests/modules.cpp: Modules_API.Doc_ReqMod_1" +module { require(logger). } - Produced declaration: - modules_require(/path/to/module, logger) + Produced declaration: modules_require("/path/to/module", logger)
- Modules Resolution: 'modules_resolution' + + + <?xxe-sn 2ahy8s1pw5c 8e?>Modules Resolution: + 'modules_resolution' - SYNTAX: + SYNTAX: **modules_resolution**(//request//, //module-resolved-ref//) (1) **modules_resolution**(//request//, //module-resolved-ref//, //module-context-ref//) (2) + + - request expressed by annotation + + + request + expressed by annotation - module-resolved-ref resolved module's - path + + + module-resolved-ref + resolved module's path - module-context-ref context module's - path + + + module-context-ref + context module's path - Xreate uses the data table to find correspondent modules for each - request. + 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 (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. + 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 + + + <?xxe-sn 2ahy8s1pw5c 8v?>See Also - Syntax: Modules + Syntax: Modules
diff --git a/documentation/Transcend/transcend.xml b/documentation/Transcend/transcend.xml index 743c93c..ad02e52 100644 --- a/documentation/Transcend/transcend.xml +++ b/documentation/Transcend/transcend.xml @@ -1,337 +1,443 @@ - Transcend + - Transcend is a compilation phase and process of reasoning about - program in order to influence compilation. + <?xxe-sn 2ajela3ur5s 7r?>Transcend - 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. + 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 + + + <?xxe-sn 2ajela3ur5s 7x?>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: + 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 + + + Literals - Strings, numbers. Example: - 5, "success" + Strings, numbers. Example: + 5, "success" - Predicates + - Predicates have zero or more arguments. Example: - final, status(broken), version("12.0.3", unstable) + Predicates + + Predicates have zero or more + arguments. Example: final, status(broken), version("12.0.3", unstable) - Negation + + + Negation - Example: -i12n, -access(allowed) + Example: -i12n, -access(allowed) - Various Xreate's entities can be annotated. At the time following - entities are supported: + Various Xreate's entities can be annotated. + At the time following entities are supported: + + + + + + - Expressions + + + Expressions and Identifiers - Code blocks and - context + Code + blocks and context - Functions and - Function + Functions + and Function Guards - Modules + Modules
- Expression and Identifiers + - SYNTAX: + <?xxe-sn 2ajela3ur5s 8v?>Expression and Identifiers + + SYNTAX: //expression// **::** //type//**;** //annotation-list//. + + - type expression's type + + + type + expression's type - annotation-list one or more annotation - attached to the expression. Annotations are delimited by - semicolon + + + annotation-list + one or more annotation attached to the expression. Annotations are + delimited by semicolon - Example: + Example: - test = function:: int + name="tests/transcend.cpp: Transcend.Doc_Expressions1" +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 + + + 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: + <?xxe-sn 2ajela3ur5s 9b?>Code Blocks and Context + + SYNTAX: **context** :: //annotations-list//. + + - annotations-list List of annotation - delimited by semicolon + + + annotations-list + List of annotation delimited by semicolon - Code block annotations called context. - Keyword context used to declare annotation within enclosing - code block. + Code block annotations called + context. Keyword + context used to declare annotation + within enclosing code block. - Example: + Example: - test = function:: int + name="tests/transcend.cpp: Transcend.Doc_Codeblocks1" +test = function:: int { context:: arithm_optimization(off). x = 10 :: int. 2 * x - 16 }
- Special Annotations + + + <?xxe-sn 2ajela3ur5s 9n?>Special Annotations - There are some annotations in Xreate that have special - meaning + There are some annotations in Xreate that + have special meaning + + - + + + - + + + - entry + - Specifies entry point of a program. See Function Syntax + entry + + Specifies entry point of a + program. See Function + Syntax - final + + + final - Specifies fixed point of loop statements. See Loop + 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: + + + <?xxe-sn 2ajela3ur5s a5?>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 + + + Explicit annotations in source + files - Annotations provided by developer in order to explicitly - affect compilation + Annotations provided by developer + in order to explicitly affect compilation - Code Analysis + - Different code analyses during compilation provide - information implicitly inferred from source code + 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 + + + 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 + + + Audit - Third party supplied external information that reflect - additional software properties, e.g. results of security - audit + Third party supplied external + information that reflect additional software properties, e.g. + results of security audit - Reasoning rules + + + Reasoning rules - Reasoning infers additional facts from pool of previously - gathered information. It is done by using resoning - rules that govern reasoning process + 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. + <?xxe-sn 2ajela3ur5s av?>Why Annotations Matter + + This section goes through differences that + annotations have over traditional statements.
- Extensible + + + <?xxe-sn 2ajela3ur5s ay?>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. + 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 + + + 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. + <?xxe-sn 2ajela3ur5s b4?>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 + + + <?xxe-sn 2ajela3ur5s b7?>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. + 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. + + + <?xxe-sn 2ajela3ur5s ba?>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. + + + <?xxe-sn 2ajela3ur5s bd?>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 + + + <?xxe-sn 2ajela3ur5s bj?>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. + 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. + 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/documentation/communication.xml b/documentation/communication.xml index 8c945f9..f4293f6 100644 --- a/documentation/communication.xml +++ b/documentation/communication.xml @@ -1,440 +1,440 @@ <?xxe-sn 29tvny21340 2?>Communication The chapter discusses safe usage of non-local variables, that is variables accessible by different components or threads with global variables as a particular case.
<?xxe-sn 29xq7jt0wzk 2?>Syntax Annotations: SYNTAX: **commop(send)** (1) **commop(receive)** (2) annotation (1) marks SEND communication event. annotation (2) marks RECEIVE communication event. Specializations: SYNTAX: **commDirect** **commGuarded** Communication reasoning able to assign following specializations: commDirect — specialization is expected to provide direct access to raw variable's content. commGaurded — specialization is expected to do internal consistency checks at run time.
<?xxe-sn 29tvny21340 4?>Background One of the major concepts that support writing of safe programs is a notion of immutability. Immutability tremendously simplifies many kinds of analyses; using immutable structures is a practical way to write multithreaded applications and has many other benefits beyond that. However in its most basic form it comes with a price of disastrous, in many cases, memory overhead, since property of immutability stipulates for each change of variable to make an independent copy of it occupying different memory region. Unwise using of immutable structures lead to the situation such that CPU is mostly occupied with unnecessary variables copying to and fro as well as with extensive garbage collection, irrelevant of actual algorithm's complexity at hand. Thus it is one of the central highlights of proper programming language design to provide techniques to overcome the shortcomings by relaxing immutability requirements keeping nevertheless safety benefits. There are many ways to approach the problem, and one such technique, namely communication model is discussed next.
<?xxe-sn 29xq7jt0wzk 6?>Communication Model Communication model is a way to capture and express what's going on with variables in a program as well as to define rules that describe valid operations over variables. Within the framework writing value to a variable is viewed as sending, and conversely reading variable's value is viewed as receiving. Variables that are accessed from different components or threads are referred to as non-local variables. This chapter is focused on a on-local variables, global variables particularly, since exactly for them it's hard to manually check exhaustively where and how they are used in order to catch any errors. It is natural to view them as the means of interaction between different parts of a program, in other words, interaction between sender and receiver, where sender and receiver are different components. The same terms comprise rules that express valid ways of interacting. The abstraction is named communication model due to similarity with the network communication. Reasoning based on working with a communication path, i.e chain of communication events(e.g. sending/receiving) occurred during program execution. Let's consider small example: - a = init():: int; comm(send). //(1) -b = a + 1 :: int; comm(receive). //(2) + a = init():: int; commop(send). //(1) +b = a + 1 :: int; commop(receive). //(2) It shows computing of variable b. Variable b depends on a so a is calculated first. Variables a, b are annotated with comm(send) and comm(receive), denoting sending and receiving events, respectively. Communication path in this case is an ordered list {<begin>, SEND, RECEIVE, <end>} where <begin>, <end> — are special events that denote first and last events in the path, respectively. The gist of using communication model is to ensure that every sent value is properly received. It relies on the compiler to gather all possible communication paths in the program as an input for processing. There are two supported modes of reasoning: Validation. In this mode all communication paths are checked against communication rules to confirm that the program is valid. Otherwise compilation error is raised. Planning. In this mode reasoning assigns proper implementation for variables in efforts to ensure validity.
<?xxe-sn 29xq7jt0wzk k?>Validation To perform validation, every communication path is checked against number of communication rules that express which communication path are valid. Default behaviour expressed by "every sent value being properly received" produce next possible cases: Valid. Path that consists of pairs of events {SEND, RECEIVE} are valid meaning that each sent value is properly received. Undefined and expired value. Paths that have parts {<begin>, RECEIVE} or {RECEIVE, RECEIVE} are invalid meaning possibly undefined value is received in the first case or duplication i.e. expired value is used in the second's one. Lost value. Paths that have parts {SEND, SEND} or {SEND, <end>} indicate possibly lost change since consequent sender replaces value in the former case and sent value is not used at all in the latter case. Traditional immutability validation is based on the idea that once valid value is valid as long it is unmodified. In this regards communication model can be viewed as an extension and more expressive tool since it also captures value expiration after it was used as well as value loss, if it was not used at all.
<?xxe-sn 29xq7jt0wzk 2a?>Planning Reasoning in the communication model aside of performing validation, also assigns appropriate specialization for sending and receiving operations, as appropriate. At the moment there are two specializations the operations are expected to support: Direct. Direct specialization commDirect is expected to provide direct access to variable's value. This specialization is assigned in case of fully statically validated communication path. Guarded. In case if there are possible communication path inconsistencies that can not be completely ruled out at compile time, checking logic should be embedded into compiled code. Specialization commGaurded is expected to hold variable state and check usage consistency.
<?xxe-sn 2a3uy8rr2f4 9?>Planning Horizon Reasoning implements algorithm that is bounded by the maximal path length it can process. The parameter is called planning horizon. Any variable that it can not check due to exceedingly large path's length is assigned default implementation commGaurded that performs necessary checks during runtime. Thus the parameter regulates trade off between static analysis extensiveness and runtime checks overhead.
<?xxe-sn 2a7t1hxqqyo 2?>Example: Direct Implementation name="tests/effects-communication.cpp: Doc_DirImpl", lines=15 import raw("scripts/dfa/propagation.lp"). import raw("scripts/dfa/polymorphism.lp"). import raw("scripts/effects-communication/communication.lp"). import raw("scripts/effects-communication/config.lp"). CommDirect = type { value:: int }. guard:: commDirect { init = function::CommDirect { {value = 0} } read = function(vault1:: CommDirect):: int { (vault1:: *;commop(receive))["value"] } write = function(vault2:: CommDirect, valueNew:: int)::CommDirect { (vault2:: *; dfa_pseudo(vault2)) + {value = valueNew}:: int; commop(send); dfa_uppy(vault2) } } main = function::int; entry { x1 = init()::*; dfa_polym(ret). x2 = write(x1, 1)::*; dfa_polym(arg). val = read(x2)::int; dfa_polym(arg). val } In this example, basic workflow is presented in main — the function write(x1, 1) is invoked following by invocation of read(x2). Functions write() and read() are annotated with commop(send) and commop(receive) respectively in order to enable communication reasoning. Analyzer gathers and validates observed communication path and since there is no ambiguity, it's possible to assign specialization CommDirect allowing direct access to the variables avoiding any additional overhead. Note, there are no any other specializations defined and if reasoning was not enable to conclude that it is the case the compilation error would be raised.
<?xxe-sn 2a7t1hxqqyo a?>Example: Guarded Implementation name="tests/effects-communication.cpp: Doc_GuardedImpl", lines=15 import raw ("scripts/effects-communication/communication.lp"). import raw ("scripts/dfa/propagation.lp"). import raw ("scripts/dfa/polymorphism.lp"). import raw ("scripts/effects-communication/config.lp"). CommState = type variant{Invalid, Valid, Outdated}. CommDirect = type { value:: int }. CommGuarded = type { value:: int, state:: CommState }. guard:: commDirect { init=function::CommDirect{ {value = 0} } read= function(vault1:: CommDirect):: int{ (vault1::CommDirect; commop(receive))["value"] } write= function(vault2:: CommDirect, valueNew1:: int)::CommDirect{ (vault2::CommDirect;dfa_pseudo(vault2)) + {value = valueNew1}:: int; commop(send); dfa_uppy(vault2) } } errorRead = function:: int { -1 } errorWrite = function:: CommGuarded{ { value = -1, state = Invalid() } } guard:: commGuarded{ init=function::CommGuarded{ { value = 0, state = Invalid() } } read=function(vault3:: CommGuarded):: int { switch variant (vault3["state"]->whatever::CommState;commop(receive)):: int case (Invalid) { errorRead() } case (Outdated) { errorRead() } case (Valid) { vault3["value"] } } write=function(vault4:: CommGuarded, valueNew2:: int)::CommGuarded{ switch variant (vault4["state"]->whatever::CommState;commop(send); dfa_pseudo(vault4))::int case (Invalid) { {value = valueNew2, state = Valid()}:: CommGuarded; dfa_uppy(vault4) } case (Outdated) { {value = valueNew2, state = Valid()}:: CommGuarded; dfa_uppy(vault4) } case (Valid) { errorWrite():: CommGuarded; dfa_uppy(vault4) } } } main=function(cmd:: num)::int; entry { x1 = init():: *; dfa_polym(ret). x2 = write(x1, 1)::*; dfa_polym(arg). x3 = if (cmd > 0)::int { y = read(x2):: int; dfa_polym(arg). y } else { z = write(x2, 2)::*; dfa_polym(arg). a = read(z):: int; dfa_polym(arg). a }. x3 } Here example of slightly more complicated workflow. Function main contains branching that depends on argument known at run time only. Analyzer is presented with two possible communication paths and one of them(false branch) leads to a possibly lost value for it contains two consequent SEND events. In this situation the analyzer unable to statically validate correctness and assigns specialization commGuarded to embed checking logic into compiled code as an intermediary layer between variable's content and client's code. Implementation commGuarded along with a variable access also tracks the variable status and returns - error if the value is inconsistent. + error if the value is inconsistent.
diff --git a/documentation/index.xml b/documentation/index.xml new file mode 100644 index 0000000..a1db8a3 --- /dev/null +++ b/documentation/index.xml @@ -0,0 +1,192 @@ + + + + + <?xxe-sn 2b06cb2u4g0 2?>Xreate Manual + + Xreate is an open source general purpose high + level programming language designed to write efficient and safe computer + programs. + + Here "high level" has very specific meaning as + an ability to easily write, read, as well as adapt software to a constantly + changing environment or business goals. In this respect, any software + product can be evaluated on the basis of the three dimensions: efficiency, + safety and adaptability. Unfortunately, those properties are proved to be + largely contradictory for it is manageable to write either efficient(still + unsafe) or safe(and impractical) code, but not both. Thus, the ultimate goal + of the language is to allow developers to produce code with all these + properties at the same time. Xreate's design principles allow to adopt any + programming technique as long as it does not improve one dimension at the + expense of another. + + To achieve aforementioned design goals Xreate + has tree distinctive layers: + + + + + + + + Brute. + Lowest layer is called Brute— + this is code that is indented to be actually compiled. Code on this + level implements actual software functionality. It resembles the usual + imperative languages' apparatus and consists of executable instructions + such as arithmetic, branching, input/output, etc. + + + + + + Transcend. + Brute level alone is not enough to constitute full fledged language + since a code requires various non executable metadata to express + developer's intents, check correctness, validity and perform other types + of analyses. In Xreate everything of this sort belongs to a layer called + Transcend. Transcend + features declarative nature that is appropriate to do management kind of + work — it analyzes, oversees and controls brute by guiding compilation + process. Everything that is on this level — logic or transcend facts and + rules are gathered and sent to an external logic solver to make + solutions that are brought back in order to guide compilation. + + + + + + Interpretation. + And there is also Interpretation + — intermediate level resembling features of dynamically typed languages + that is used as a contact point and interpreter between brute and + transcend and their low level and high level constructs and structures + respectively. + + + + On a syntactic level Xreate is procedural + language with extensive use of annotations + — arbitrary unconstrained metadata that software developer can attach to a + different language constructs, variables and code blocks. Annotations are + completely invisible for the compiler proper and used by transcend rather as + suggestions conveying additional information. + + Most severe and + hard to resolve problems in software development stem from interaction of + different components on a different levels. Each individual component often + is thoroughly tested and works reliably. But combining them with complex + interaction and extensive IO to build end-user software usually leads to a + range of runtime errors, security vulnerabilities and performance + degradation. Thus key emphasis of Xreate is on domain specific component + level improvements and joint use of external resources. This aspect is + covered in exploitation + and containers + chapters. Unlike academic languages, Xreate targets safe and reliable usage + of effectful computations, such as IO, covered in virtualization, + exploitation + chapters and mutable structures covered in communication. + +
+ + + <?xxe-sn 2b06cb2u4g0 4?>Basic Example + + To demonstrate what Xreate is all about, + basic example is given below: + + name="tests/introduction.cpp: Introduction.Doc_Example_1", lines=15 +guard:: iAmVeryFast +{ + div = function(a:: float, b:: float):: float + { + a / b + } +} + +guard:: iAmVerySafe +{ + div = function(a:: float, b:: float):: float + { + if ( b == (0::float)):: float {0::float} else {a / b} + } +} + +test = function:: float; entry; iAmVerySecure +{ + div(10, 5) +} + + Here entry point of the program is a + function test recognized so by the + compiler because of annotation entry + in its signature. There are also two functions div + called specializations. Each + specialization has a guard that defines condition that has to be met in + order to invoke this particular specialization. In the example, + specializations of div have + iAmVeryFast and iAmVerySafe + guards respectively. Let's say, developer provides two specializations + where first is a very fast division implementation and second one is a + very safe since it checks division by zero but at the same time is + unbearably slow due to extra check instruction. This is a basis of polymorphism + — client's code test is able to work + with any specialization, and compiler should decide which one to invoke + with the only hint it has — annotation iAmVerySecure + in the function test's signature. + + + + + + All annotations(except entry) + are custom defined by developer itself. + + + This is when transcend comes into play. By + adding transcend rule as shown below it is possible to associate + annotation iAmVerySecure with + invocation of specialization guarded by iAmVerySafe: + + name="tests/introduction.cpp: Introduction.Doc_Example_1", lines=15 +dfa_callguard(SiteInv, iAmVerySafe):- + dfa_callfn(SiteInv, div); + SiteInv = s(_, _, ScopeInv); + cfa_parent(ScopeInv, function(FnInv)); + bind_func(FnInv, iAmVerySecure). + + Transcend rules are written in ASP syntax — + common syntax to write logic programs. This particular rule reads that for + any function annotated with iAmVerySecure, + certain specialization iAmVerySafe is + chosen for div invocation. + + + + + In this example an appropriate + specialization is statically resolved, so other specialization isn't + even compiled. + + + By providing custom rules it is possible to + implement any polymorphism strategy, be it statically performed or + dynamically. The example demonstrates basic workflow: transcend gathers + available information about a program, such as annotations and using + custom rules makes decisions to guide compilation process particularly by + selecting appropriate specializations as in this example. +
+
diff --git a/git-commit-template b/git-commit-template index 567e7b8..38a341e 100644 --- a/git-commit-template +++ b/git-commit-template @@ -1,12 +1,14 @@ - +**REMEMBER TO UPDATE DOCUMENTATION ON THE SITE** -Syntax: -Transcend: -Compilation: + + +Syntax: Documentation: -: <...> +Transcend: +Compilation: -Malfunctions: +: <...> -Tests: +Malfunctions: +Tests: diff --git a/installation/docker/patches/potassco-patch-95cc11 b/installation/docker/patches/potassco-patch-95cc11 new file mode 100644 index 0000000..391b1b5 --- /dev/null +++ b/installation/docker/patches/potassco-patch-95cc11 @@ -0,0 +1,81 @@ +diff --git a/SConscript b/SConscript +index 0ab4e7a..8341dd8 100644 +--- a/SConscript ++++ b/SConscript +@@ -183,21 +183,21 @@ DEFS = {} + failure = False + + if not conf.CheckBison(): +- print 'error: no usable bison version found' ++ print ('error: no usable bison version found') + failure = True + + if not conf.CheckRe2c(): +- print 'error: no usable re2c version found' ++ print ('error: no usable re2c version found') + failure = True + + if not conf.CheckCXX(): +- print 'error: no usable C++ compiler found' +- print "Please check the log file for further information: " + log_file ++ print ('error: no usable C++ compiler found') ++ print ("Please check the log file for further information: " + log_file) + Exit(1) + + if not conf.CheckSHCXX(): +- print 'error: no usable (shared) C++ compiler found' +- print "Please check the log file for further information: " + log_file ++ print ('error: no usable (shared) C++ compiler found') ++ print ("Please check the log file for further information: " + log_file) + Exit(1) + + if not conf.CheckThreadLocal(): +@@ -211,7 +211,7 @@ if env['WITH_PYTHON'] == "auto": + DEFS["WITH_PYTHON"] = 1 + elif env['WITH_PYTHON']: + if not conf.CheckLibs("python", env['WITH_PYTHON'], "Python.h"): +- print 'error: python library not found' ++ print ('error: python library not found') + failure = True + else: + with_python = True +@@ -224,7 +224,7 @@ if env['WITH_LUA'] == "auto": + DEFS["WITH_LUA"] = 1 + elif env['WITH_LUA']: + if not conf.CheckLibs("lua", env['WITH_LUA'], "lua.hpp"): +- print 'error: lua library not found' ++ print ('error: lua library not found') + failure = True + else: + with_lua = True +@@ -263,7 +263,7 @@ if env['WITH_THREADS'] is not None: + elif env['WITH_THREADS'] == "windows": + pass # nohing to do + else: +- print 'error: unknown thread model' ++ print ('error: unknown thread model') + failure = True + + +@@ -273,7 +273,7 @@ claspEnv.Append(CPPDEFINES=DEFS) + # {{{1 Check configuration + + if failure: +- print "Please check the log file for further information: " + log_file ++ print ("Please check the log file for further information: " + log_file) + Exit(1) + + # {{{1 Opts: Library +diff --git a/libprogram_opts/src/string_convert.cpp b/libprogram_opts/src/string_convert.cpp +index 5e35424..16217de 100644 +--- a/libprogram_opts/src/string_convert.cpp ++++ b/libprogram_opts/src/string_convert.cpp +@@ -53,7 +53,7 @@ typedef _locale_t locale_t; + #define freelocale _free_locale + inline locale_t default_locale() { return _create_locale(LC_ALL, "C"); } + #else +-#include ++#include + inline locale_t default_locale() { return newlocale(LC_ALL_MASK, "C", 0); } + #endif + static struct LocaleHolder { diff --git a/installation/prepare-opensuse-leap15 b/installation/prepare-opensuse-leap15 new file mode 100644 index 0000000..9100666 --- /dev/null +++ b/installation/prepare-opensuse-leap15 @@ -0,0 +1,65 @@ +#!/bin/bash + +# VERSIONS OF MAJOR DEPENDENCIES: +# OPENSUSE LEAP 15 +# LLVM: 5.0.1 +# CLANG: 5.0.1 +# GCC: 7.3.1 + +CLINGO_PATH=/opt/potassco/clingo +COCO_PATH=/opt/coco +XREATE_PATH=/opt/xreate + +CLASP_VERSION=95cc1182f + +# PREREQUISITES +# +sudo zypper in \ + git wget unzip make \ + gcc7-c++ scons bison re2c\ + llvm5-devel libboost_headers1_66_0-devel \ + cmake gtest tbb-devel clang5-devel \ + libxml2-devel libboost_system1_66_0-devel libboost_filesystem1_66_0-devel + + +# COCO CPP +# The Compiler Generator Coco/R +# http://www.ssw.uni-linz.ac.at/coco/#CPP +# +mkdir $COCO_PATH +cd $COCO_PATH +wget http://www.ssw.uni-linz.ac.at/coco/CPP/CocoSourcesCPP.zip +unzip ./CocoSourcesCPP.zip +make + + +# CLINGO +# A grounder and solver for logic programs. +# https://github.com/potassco/clingo +# +mkdir -p $CLINGO_PATH +cd $CLINGO_PATH +git clone https://github.com/potassco/clingo.git ./ &&\ +git reset --hard $CLASP_VERSION +git apply $XREATE_PATH/installation/docker/patches/potassco-patch-95cc11 +scons configure --build-dir=debug &&\ + sed -i "s/, '-fvisibility=hidden'//" build/debug.py &&\ + sed -i "s/CXXFLAGS = \[\(.*\)\]/CXXFLAGS = \['-fPIC', \1\]/" build/debug.py &&\ + sed -i "s/WITH_LUA = 'auto'/WITH_LUA = None/" build/debug.py &&\ + sed -i "s/WITH_PYTHON = 'auto'/WITH_PYTHON = None/" build/debug.py &&\ + sed -i "s/'-std=c++11'/'-std=c++14'/" build/debug.py &&\ + cat build/debug.py &&\ + scons --build-dir=debug + + +# XREATE +# +cd $XREATE_PATH/vendors/jeayeson +./configure +mkdir ../../build +cd ../../build +ln -s $COCO_PATH/Copyright.frame $XREATE_PATH/vendors/coco/generator/Copyright.frame +ln -s $COCO_PATH/Scanner.frame $XREATE_PATH/vendors/coco/generator/Scanner.frame +mkdir $XREATE_PATH/grammar/main $XREATE_PATH/grammar/modules +cmake -DCMAKE_BUILD_TYPE=Debug -DBUILD_XREATE_TESTS=1 -DCOCO_EXECUTABLE=$COCO_PATH/Coco -DCOCO_FRAMES_PATH=$XREATE_PATH/vendors/coco/generator/ -DCMAKE_CXX_COMPILER=g++ ../cpp + diff --git a/scripts/containers/containers.lp b/scripts/containers/containers.lp index 2a05f4d..4d1b17a 100644 --- a/scripts/containers/containers.lp +++ b/scripts/containers/containers.lp @@ -1,87 +1,93 @@ % This Source Code Form is subject to the terms of the Mozilla Public % License, v. 2.0. If a copy of the MPL was not distributed with this % file, You can obtain one at http://mozilla.org/MPL/2.0/. -%defines - impl(solid; llvm_const_array; on_the_fly). - op(seqaccess). op(randaccess). - - relation(recommends; satisfied; unsupported). - relation_score(satisfied, 0). - relation_score(recommends, 1). - relation_score(unsupported, -1). - score(-1..1). + %DEFINES +container(solid; onthefly; llvm_const_array). +access(serial; rand). +relation(recommends; satisfied; unsupported). +score(-1..1). + - %domain facts: -relation_op(seqaccess, on_the_fly, recommends). -relation_op(randaccess, llvm_const_array, recommends). -relation_op(randaccess, on_the_fly, unsupported). + %DOMAIN FACTS +relation_score( + unsupported, -1; + satisfied, 0; + recommends, 1 +). + +relation_access( + serial, onthefly, recommends; + rand, llvm_const_array, recommends; + rand, onthefly, unsupported +). + %AST ATTACHMENTS -attach_operator_map(none, op(seqaccess)). -attach_operator_fold(none, op(seqaccess), none). +attach_operator_map(none, access(serial)). +attach_operator_fold(none, access(serial), none). -attach_operator_list_range(impl(on_the_fly)). -attach_operator_list(impl(solid)). -attach_operator_index(op(randaccess)). +attach_operator_list_range(container(onthefly)). +attach_operator_list(container(solid)). +attach_operator_index(access(rand)). %CLUSTERS cluster_link(VTo, VFrom):- %aliases dfa_connection(VTo, VFrom, strong). cluster_link(VResult, VSource):- %Operator MAP ast_op_map(VResult, VSource, _). cluster_root(V):- not cluster_link(V, _); v(V). var_cluster(V, V):- %self referencing cluster_root(V). var_cluster(VRoot, VTo):- %propagation down the tree cluster_link(VTo, VFrom); var_cluster(VRoot, VFrom). %ALGORITHM -impl_fulfill(OP, IMPL):- relation_op(OP, IMPL, unsupported). impl_fulfill(OP, IMPL, SCORE):- SCORE = #sum{SCORE1, (OP, IMPL, RL): relation_op(OP, IMPL, RL), relation_score(RL, SCORE1)}; not -impl_fulfill(OP, IMPL); - op(OP); impl(IMPL). + access(OP); container(IMPL). -impl_fulfill_cluster(Var0, Impl):- var_cluster(Var0, Var_Any); - bind(Var_Any, op(Op)); + bind(Var_Any, access(Op)); -impl_fulfill(Op, Impl). impl_fulfill_cluster(VAR0, IMPL, Score):- - Score = #sum{SCORE, (OP, IMPL, VAR_ANY): impl_fulfill(OP, IMPL, SCORE), var_cluster(VAR0, VAR_ANY), bind(VAR_ANY, op(OP))}; - bind(VAR0, impl(IMPL)); + Score = #sum{SCORE, (OP, IMPL, VAR_ANY): impl_fulfill(OP, IMPL, SCORE), var_cluster(VAR0, VAR_ANY), bind(VAR_ANY, access(OP))}; + bind(VAR0, container(IMPL)); cluster_root(VAR0); not -impl_fulfill_cluster(VAR0, IMPL). %OUTPUT containers_impl(V, Impl):- containers_impl(VRoot, Impl); var_cluster(VRoot, V). containers_impl(V, solid(Size)):- impl_fulfill_cluster(V, solid, _); ast_op_list(V, Size); cluster_root(V). -containers_impl(V, on_the_fly):- - impl_fulfill_cluster(V, on_the_fly, _); +containers_impl(V, onthefly):- + impl_fulfill_cluster(V, onthefly, _); ast_op_list_range(V); cluster_root(V). %optimization % #maximize {SCORE, (VAR0, IMPL) : impl_fulfill_cluster(VAR0, IMPL, SCORE)}. #show var_cluster/2. #show impl_fulfill_cluster/3. diff --git a/vendors/coco/generator/Copyright.frame b/vendors/coco/generator/Copyright.frame deleted file mode 120000 index 708b5d5..0000000 --- a/vendors/coco/generator/Copyright.frame +++ /dev/null @@ -1 +0,0 @@ -/usr/share/coco-cpp/Copyright.frame \ No newline at end of file diff --git a/vendors/coco/generator/Scanner.frame b/vendors/coco/generator/Scanner.frame deleted file mode 120000 index d1ee132..0000000 --- a/vendors/coco/generator/Scanner.frame +++ /dev/null @@ -1 +0,0 @@ -/usr/share/coco-cpp/Scanner.frame \ No newline at end of file