diff --git a/config/default.json b/config/default.json index 8043ba5..631feec 100644 --- a/config/default.json +++ b/config/default.json @@ -1,74 +1,74 @@ { "containers": { "id": { "implementations": "containers_impl", "linkedlist": "linkedlist" }, "impl": { "solid": "solid", "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": "default", "templates": { - "troubleshooting":"Example.Example1", + "troubleshooting":"Exploitation.Doc_ExampleEov_1", "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/aux/xreatemanager-decorators.cpp b/cpp/src/aux/xreatemanager-decorators.cpp index 07bd480..2b4038d 100644 --- a/cpp/src/aux/xreatemanager-decorators.cpp +++ b/cpp/src/aux/xreatemanager-decorators.cpp @@ -1,75 +1,77 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * xreatemanager-decorators.cpp * * Author: pgess * Created on July 16, 2017, 4:40 PM */ /** * \file xreatemanager-decorators.h * \brief \ref xreate::XreateManager decorators to provide various functionality */ #include "aux/xreatemanager-decorators.h" #include "main/Parser.h" #include "pass/compilepass.h" #include "pass/cfapass.h" #include "pass/dfapass.h" #include "pass/interpretationpass.h" #include "pass/versionspass.h" +#include "pass/cfatemporalseqpass.h" namespace xreate{ void XreateManagerDecoratorBase::initPasses() { } void XreateManagerDecoratorBase::prepareCode(std::string&& code) { grammar::main::Scanner scanner(reinterpret_cast (code.c_str()), code.size()); grammar::main::Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Parser errors"); PassManager::prepare(parser.root->finalize()); } void XreateManagerDecoratorBase::prepareCode(FILE* code) { grammar::main::Scanner scanner(code); grammar::main::Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Parser errors"); PassManager::prepare(parser.root->finalize()); } void XreateManagerDecoratorBase::analyse() { CompilePass::prepareQueries(transcend); transcend->run(); } void XreateManagerDecoratorFull::initPasses() { cfa::CFAPass* passCFG = new cfa::CFAPass(this); registerPass(new dfa::DFAPass(this), PassId::DFAPass); registerPass(passCFG, PassId::CFAPass); registerPass(new interpretation::InterpretationPass(this), PassId::InterpretationPass); registerPass(new versions::VersionsPass(this), PassId::VersionsPass); + registerPass(new cfa::CFATemporalSeqPass(this), PassId::CFATemporalSeqPass); } void* XreateManagerDecoratorFull::run() { transcend->deleteReports(); std::unique_ptr compiler(new compilation::CompilePassCustomDecorators<>(this)); compiler->run(); llvm->print(); llvm->initJit(); return llvm->getFunctionPointer(compiler->getEntryFunction()); } } //namespace xreate diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp index 55384ef..06049f2 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,780 +1,788 @@ /* 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 Main compilation routine. See \ref xreate::CompilePass */ #include "compilepass.h" #include "transcendlayer.h" #include #include "llvmlayer.h" #include "query/containers.h" #include "compilation/containers.h" #include "ExternLayer.h" #include "compilation/targetinterpretation.h" #include "pass/versionspass.h" #include "compilation/scopedecorators.h" #include "compilation/operators.h" #include "compilation/latex.h" #include "analysis/typeinference.h" #include #include #include using namespace std; using namespace llvm; namespace xreate{ namespace compilation{ std::string BasicFunctionUnit::prepareName() { AST* ast = IFunctionUnit::pass->man->root; string name = ast->getFunctionSpecializations(IFunctionUnit::function->__name).size() > 1 ? IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : IFunctionUnit::function->__name; return name; } std::vector BasicFunctionUnit::prepareSignature() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; std::vector signature; std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()), [llvm, ast, entry](const std::string & arg)->llvm::Type* { assert(entry->__identifiers.count(arg)); ScopedSymbol argid{entry->__identifiers.at(arg), versions::VERSION_NONE}; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type)); }); return signature; } llvm::Type* BasicFunctionUnit::prepareResult() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type)); } llvm::Function::arg_iterator BasicFunctionUnit::prepareBindings() { CodeScope* entry = IFunctionUnit::function->__entry; ICodeScopeUnit* entryCompilation = IFunctionUnit::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = IFunctionUnit::raw->arg_begin(); for (std::string &arg : entry->__bindings) { ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, argid); fargsI->setName(arg); ++fargsI; } return fargsI; } //DEBT compiler rigidly depends on exact definition of DefaultFunctionUnit typedef latex::LatexBruteFunctionDecorator< compilation::BasicFunctionUnit> BruteFunctionDefault; ICodeScopeUnit::ICodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : pass(compilePass), function(f), scope(codeScope), currentBlockRaw(nullptr) { } llvm::Value* BruteFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { llvm::Function* calleeInfo = dyn_cast(__callee); if (calleeInfo) { auto argsFormal = calleeInfo->args(); size_t sizeArgsF = std::distance(argsFormal.begin(), argsFormal.end()); assert(args.size() >= sizeArgsF); assert(calleeInfo->isVarArg() || args.size() == sizeArgsF); auto argFormal = argsFormal.begin(); for(size_t argId = 0; argId < args.size(); ++argId){ if(argFormal != argsFormal.end()){ args[argId] = typeinference::doAutomaticTypeConversion( args.at(argId), argFormal->getType(), llvm->builder); ++argFormal; } } } //Do not name function call that returns Void. std::string nameStatement = hintDecl; if (calleeInfo->getReturnType()->isVoidTy()) { nameStatement.clear(); } return llvm->builder.CreateCall(__calleeTy, __callee, args, nameStatement); } //DESABLEDFEATURE implement inlining class CallStatementInline : public IFnInvocation{ public: CallStatementInline(IFunctionUnit* caller, IFunctionUnit* callee, LLVMLayer* l) : __caller(caller), __callee(callee), llvm(l) { } llvm::Value* operator()(std::vector&& args, const std::string& hintDecl) { //TOTEST inlining // CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry); // for(int i=0, size = args.size(); ibindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i))); // } // // // return entryCompilation->compile(); return nullptr; } private: IFunctionUnit* __caller; IFunctionUnit* __callee; LLVMLayer* llvm; bool isInline() { // Symbol ret = Symbol{0, function->__entry}; // bool flagOnTheFly = SymbolAttachments::get(ret, false); //TODO consider inlining return false; } } ; BasicCodeScopeUnit::BasicCodeScopeUnit(const CodeScope * const codeScope, IFunctionUnit* f, CompilePass* compilePass) : ICodeScopeUnit(codeScope, f, compilePass) { } llvm::Value* BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { Expression declaration = CodeScope::getDefinition(s); const CodeScope* scopeExternal = s.scope; ICodeScopeUnit* scopeBruteExternal = ICodeScopeUnit::function->getScopeUnit(scopeExternal); assert(scopeBruteExternal->currentBlockRaw); llvm::Value* resultRaw; llvm::BasicBlock* blockOwn = pass->man->llvm->builder.GetInsertBlock(); if (scopeBruteExternal->currentBlockRaw == blockOwn) { resultRaw = scopeBruteExternal->process(declaration, hintRetVar); scopeBruteExternal->currentBlockRaw = currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); } else { pass->man->llvm->builder.SetInsertPoint(scopeBruteExternal->currentBlockRaw); resultRaw = scopeBruteExternal->processSymbol(s, hintRetVar); pass->man->llvm->builder.SetInsertPoint(blockOwn); } return resultRaw; } IFnInvocation* BasicCodeScopeUnit::findFunction(const Expression& opCall) { const std::string& calleeName = opCall.getValueString(); LLVMLayer* llvm = pass->man->llvm; const std::list& specializations = pass->man->root->getFunctionSpecializations(calleeName); //if no specializations registered - check external function if (specializations.size() == 0) { llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName); llvm::outs() << "Debug/External function: " << calleeName; external->getType()->print(llvm::outs(), true); llvm::outs() << "\n"; return new BruteFnInvocation(external, llvm); } //There should be only one specialization without any valid guards at this point return new BruteFnInvocation(pass->getFunctionUnit( pass->man->root->findFunction(calleeName))->compile(), llvm); } //DISABLEDFEATURE transformations // if (pass->transformations->isAcceptable(expr)){ // return pass->transformations->transform(expr, result, ctx); // } llvm::Value* BasicCodeScopeUnit::process(const Expression& expr, const std::string& hintVarDecl) { #define DEFAULT(x) (hintVarDecl.empty()? x: hintVarDecl) llvm::Value *left; llvm::Value *right; LLVMLayer& l = *pass->man->llvm; xreate::compilation::AdvancedInstructions instructions = xreate::compilation::AdvancedInstructions({this, function, pass}); switch (expr.op) { case Operator::SUB: case Operator::MUL: case Operator::DIV: case Operator::EQU: case Operator::LSS: case Operator::GTR: case Operator::NE: case Operator::LSE: case Operator::GTE: assert(expr.__state == Expression::COMPOUND); assert(expr.operands.size() == 2); left = process(expr.operands[0]); right = process(expr.operands[1]); break; default:; } switch (expr.op) { case Operator::ADD: { left = process(expr.operands[0]); Context context{this, function, pass}; llvm::Value* resultSU = StructUpdate::add(expr.operands[0], left, expr.operands[1], context, DEFAULT("tmp_add")); if (resultSU) return resultSU; right = process(expr.operands[1]); llvm::Value* resultAddPA = pointerarithmetic::PointerArithmetic::add(left, right, context, DEFAULT("tmp_add")); if (resultAddPA) { return resultAddPA; } return l.builder.CreateAdd(left, right, DEFAULT("tmp_add")); break; } case Operator::SUB: return l.builder.CreateSub(left, right, DEFAULT("tmp_sub")); break; case Operator::MUL: return l.builder.CreateMul(left, right, DEFAULT("tmp_mul")); break; case Operator::DIV: if (left->getType()->isIntegerTy()) return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div")); if (left->getType()->isFloatingPointTy()) return l.builder.CreateFDiv(left, right, DEFAULT("tmp_div")); break; case Operator::EQU: { if (left->getType()->isIntegerTy()) return l.builder.CreateICmpEQ(left, right, DEFAULT("tmp_equ")); if (left->getType()->isFloatingPointTy()) return l.builder.CreateFCmpOEQ(left, right, DEFAULT("tmp_equ")); const ExpandedType& leftT = pass->man->root->getType(expr.operands[0]); const ExpandedType& rightT = pass->man->root->getType(expr.operands[0]); if(leftT->__operator == TypeOperator::VARIANT && rightT->__operator == TypeOperator::VARIANT){ llvm::Type* selectorT = llvm::cast(left->getType())->getElementType(0); llvm::Value* leftUnwapped = typeinference::doAutomaticTypeConversion(left, selectorT, l.builder); llvm::Value* rightUnwapped = typeinference::doAutomaticTypeConversion(right, selectorT, l.builder); return l.builder.CreateICmpEQ(leftUnwapped, rightUnwapped, DEFAULT("tmp_equ")); } break; } case Operator::NE: return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne")); break; case Operator::LSS: return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss")); break; case Operator::LSE: return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse")); break; case Operator::GTR: return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr")); break; case Operator::GTE: return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte")); break; case Operator::NEG: + { left = process(expr.operands[0]); - return l.builder.CreateNeg(left, DEFAULT("tmp_neg")); + ExpandedType leftTy = pass->man->root->getType(expr.operands[0]); + + if (leftTy->__value == TypePrimitive::Bool){ + return l.builder.CreateNot(left, DEFAULT("tmp_not")); + } else { + return l.builder.CreateNeg(left, DEFAULT("tmp_neg")); + } break; + } case Operator::CALL: { assert(expr.__state == Expression::COMPOUND); shared_ptr callee(findFunction(expr)); const std::string& nameCallee = expr.getValueString(); //prepare arguments std::vector args; args.reserve(expr.operands.size()); std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()), [this](const Expression & operand) { return process(operand); } ); return (*callee)(move(args), DEFAULT("res_" + nameCallee)); } case Operator::IF: { return instructions.compileIf(expr, DEFAULT("tmp_if")); } case Operator::SWITCH: { return instructions.compileSwitch(expr, DEFAULT("tmp_switch")); } case Operator::LOGIC_AND: { assert(expr.operands.size() == 1); return process(expr.operands[0]); } case Operator::LIST: { ExpandedType exprT = l.ast->getType(expr); bool flagIsArray; do { if (exprT->__operator == TypeOperator::CUSTOM){ if (l.layerExtern->isArrayType(exprT->__valueCustom)){ flagIsArray = true; break; } if (l.layerExtern->isRecordType(exprT->__valueCustom)){ flagIsArray = false; break; } assert(false && "Inapproriate external type"); } if (exprT->__operator != TypeOperator::LIST_ARRAY && exprT->__operator != TypeOperator::LIST_RECORD){ assert(false && "Inapproriate type"); } flagIsArray = exprT->__operator == TypeOperator::LIST_ARRAY; } while(false); if(flagIsArray){ return instructions.compileListAsSolidArray(expr, DEFAULT("tmp_list")); } const std::vector fieldsFormal = (exprT.get().__operator == TypeOperator::CUSTOM) ? l.layerExtern->getStructFields(l.layerExtern->lookupType(exprT.get().__valueCustom)) : exprT.get().fields; std::map indexFields; for (size_t i = 0, size = fieldsFormal.size(); i < size; ++i) { indexFields.emplace(fieldsFormal[i], i); } llvm::StructType* tyLiteralRaw = llvm::cast(l.toLLVMType(exprT)); llvm::Value* record = llvm::UndefValue::get(tyLiteralRaw); for (size_t i = 0; i < expr.operands.size(); ++i) { const Expression& operand = expr.operands.at(i); unsigned int fieldId = indexFields.at(expr.bindings.at(i)); llvm::Value* result = process(operand); assert(result); record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef({fieldId})); } return record; }; case Operator::LIST_RANGE: { assert(false); //no compilation phase for a range list // return InstructionList(this).compileConstantArray(expr, l, hintRetVar); }; case Operator::MAP: { assert(expr.blocks.size()); return instructions.compileMapSolidOutput(expr, DEFAULT("map")); }; case Operator::FOLD: { return instructions.compileFold(expr, DEFAULT("fold")); }; case Operator::INF: { return instructions.compileFoldInf(expr, DEFAULT("fold")); }; case Operator::INDEX: { //TASK allow multiindex compilation assert(expr.operands.size() == 2); assert(expr.operands[0].__state == Expression::IDENT); const std::string& hintIdent = expr.operands[0].getValueString(); Symbol s = Attachments::get(expr.operands[0]); const ExpandedType& t2 = pass->man->root->getType(expr.operands[0]); llvm::Value* aggr = processSymbol(s, hintIdent); switch (t2.get().__operator) { case TypeOperator::LIST_RECORD: case TypeOperator::CUSTOM: { std::string idxField; const Expression& idx = expr.operands.at(1); switch (idx.__state) { //named struct field case Expression::STRING: idxField = idx.getValueString(); break; //anonymous struct field case Expression::NUMBER: idxField = to_string((int) idx.getValueDouble()); break; default: assert(false && "Wrong index for a struct"); } return instructions.compileStructIndex(aggr, t2, idxField); }; case TypeOperator::LIST_ARRAY: { std::vector indexes; std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()), [this] (const Expression & op) { return process(op); } ); return instructions.compileArrayIndex(aggr, indexes, DEFAULT(string("el_") + hintIdent)); }; default: assert(false); } }; case Operator::CALL_INTRINSIC: { const std::string op = expr.getValueString(); if (op == "copy") { llvm::Value* result = process(expr.getOperands().at(0)); auto decoratorVersions = Decorators::getInterface(this); llvm::Value* storage = decoratorVersions->processIntrinsicInit(result->getType()); decoratorVersions->processIntrinsicCopy(result, storage); return l.builder.CreateLoad(storage, hintVarDecl); } assert(false && "undefined intrinsic"); } case Operator::QUERY: case Operator::QUERY_LATE: { assert(false && "Should be processed by interpretation"); } case Operator::VARIANT: { const ExpandedType& typVariant = pass->man->root->getType(expr); llvm::Type* typVariantRaw = l.toLLVMType(typVariant); llvm::Type* typIdRaw = llvm::cast(typVariantRaw)->getElementType(0); uint64_t id = expr.getValueDouble(); llvm::Value* variantRaw = llvm::UndefValue::get(typVariantRaw); variantRaw = l.builder.CreateInsertValue(variantRaw, llvm::ConstantInt::get(typIdRaw, id), llvm::ArrayRef({0})); const bool flagDoReference = expr.operands.size(); if (flagDoReference) { const ExpandedType& subtyp = ExpandedType(typVariant->__operands.at(id)); llvm::Type* subtypRaw = l.toLLVMType(subtyp); Attachments::put(expr.operands.at(0), subtyp); llvm::Value* subtypValue = process(expr.operands.at(0)); llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); llvm::Value* addrAsStorage = l.builder.CreateAlloca(typStorageRaw); llvm::Value* addrAsSubtyp = l.builder.CreateBitOrPointerCast(addrAsStorage, subtypRaw->getPointerTo()); l.builder.CreateStore(subtypValue, addrAsSubtyp); llvm::Value* storageRaw = l.builder.CreateLoad(typStorageRaw, addrAsStorage); variantRaw = l.builder.CreateInsertValue(variantRaw, storageRaw, llvm::ArrayRef({1})); } return variantRaw; } case Operator::SWITCH_VARIANT: { return instructions.compileSwitchVariant(expr, DEFAULT("tmpswitch")); } case Operator::SWITCH_LATE: { assert(false && "Instruction's compilation should've been redirected to interpretation"); return nullptr; } case Operator::SEQUENCE: { return instructions.compileSequence(expr); } case Operator::UNDEF: { llvm::Type* typExprUndef = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); return llvm::UndefValue::get(typExprUndef); } case Operator::INVALID: assert(expr.__state != Expression::COMPOUND); switch (expr.__state) { case Expression::IDENT: { Symbol s = Attachments::get(expr); return processSymbol(s, expr.getValueString()); } case Expression::NUMBER: { llvm::Type* typConst = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); int literal = expr.getValueDouble(); if (typConst->isFloatingPointTy()) return llvm::ConstantFP::get(typConst, literal); if (typConst->isIntegerTy()) return llvm::ConstantInt::get(typConst, literal); assert(false && "Can't compile literal"); } case Expression::STRING: { return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str")); }; default: { break; } }; break; default: break; } assert(false && "Can't compile expression"); return 0; } llvm::Value* BasicCodeScopeUnit::compile(const std::string& hintBlockDecl) { LLVMLayer* llvm = pass->man->llvm; if (!hintBlockDecl.empty()) { llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm->llvmContext, hintBlockDecl, function->raw); pass->man->llvm->builder.SetInsertPoint(block); } currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope}; return processSymbol(symbScope); } ICodeScopeUnit::~ICodeScopeUnit() { } IFunctionUnit::~IFunctionUnit() { } llvm::Function* IFunctionUnit::compile() { if (raw != nullptr) return raw; LLVMLayer* llvm = pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; string&& functionName = prepareName(); std::vector&& types = prepareSignature(); llvm::Type* expectedResultType = prepareResult(); llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false); raw = llvm::cast(llvm->module->getOrInsertFunction(functionName, ft)); prepareBindings(); const std::string&blockName = "entry"; llvm::BasicBlock* blockCurrent = builder.GetInsertBlock(); llvm::Value* result = getScopeUnit(function->__entry)->compile(blockName); assert(result); //SECTIONTAG types/convert function ret value builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->builder)); if (blockCurrent) { builder.SetInsertPoint(blockCurrent); } llvm->moveToGarbage(ft); return raw; } ICodeScopeUnit* IFunctionUnit::getScopeUnit(const CodeScope * const scope) { if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result) { return result.get(); } } std::shared_ptr unit(pass->buildCodeScopeUnit(scope, this)); if (scope->__parent != nullptr) { auto parentUnit = Decorators::getInterface(getScopeUnit(scope->__parent)); parentUnit->registerChildScope(unit); } else { __orphanedScopes.push_back(unit); } if (!__scopes.emplace(scope, unit).second) { __scopes[scope] = unit; } return unit.get(); } ICodeScopeUnit* IFunctionUnit::getScopeUnit(ManagedScpPtr scope) { return getScopeUnit(&*scope); } ICodeScopeUnit* IFunctionUnit::getEntry() { return getScopeUnit(function->getEntryScope()); } template<> compilation::IFunctionUnit* CompilePassCustomDecorators ::buildFunctionUnit(const ManagedFnPtr& function) { return new BruteFunctionDefault(function, this); } template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators ::buildCodeScopeUnit(const CodeScope * const scope, IFunctionUnit* function) { return new DefaultCodeScopeUnit(scope, function, this); } } // end of compilation compilation::IFunctionUnit* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { compilation::IFunctionUnit* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } void CompilePass::run() { //Initialization: managerTransformations = new xreate::compilation::TransformationsManager(); targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); //Determine entry function: StaticModel model = man->transcend->query(Config::get("function-entry")); assert(model.size() && "Error: No entry function found"); assert(model.size() == 1 && "Error: Ambiguous entry function"); string nameMain = std::get<0>(TranscendLayer::parse(model.begin()->second)); compilation::IFunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain)); //Compilation itself: entry = unitMain->compile(); } llvm::Function* CompilePass::getEntryFunction() { assert(entry); return entry; } void CompilePass::prepareQueries(TranscendLayer* transcend) { transcend->registerQuery(new containers::Query(), QueryId::ContainersQuery); transcend->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery); transcend->registerQuery(new latex::LatexQuery(), QueryId::LatexQuery); } } //end of namespace xreate /** * \class xreate::CompilePass * \brief The owner of the compilation process. Performs fundamental compilation activities along with the xreate::compilation's routines * * xreate::CompilePass traverses over xreate::AST tree and produces executable code. * The pass performs compilation using the following data sources: * - %Attachments: the data gathered by the previous passes. See \ref xreate::Attachments. * - Transcend solutions accessible via queries. See \ref xreate::IQuery, \ref xreate::TranscendLayer. * * The pass generates a bytecode by employing \ref xreate::LLVMLayer(wrapper over LLVM toolchain). * Many compilation activities are delegated to more specific routines. Most notable delegated compilation aspects are: * - Containers support. See \ref xreate::containers. * - Latex compilation. See \ref xreate::latex. * - Interpretation support. See \ref xreate::interpretation. * - Loop saturation support. See \ref xreate::compilation::TransformationsScopeDecorator. * - External code interaction support. See \ref xreate::ExternLayer (wrapper over Clang library). * * \section adaptability_sect Adaptability * xreate::CompilePass's behaviour can be adapted in several ways: * - %Function Decorators to alter function-level compilation. See \ref xreate::compilation::IFunctionUnit * - Code Block Decorators to alter code block level compilation. See \ref xreate::compilation::ICodeScopeUnit. * Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit * - Targets to allow more versitile extensions. * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See \ref xreate::compilation::Target. * - Altering %function invocation. See \ref xreate::compilation::IFnInvocation. * * Clients are free to construct a compiler instantiation with the desired decorators by using \ref xreate::compilation::CompilePassCustomDecorators. * As a handy alias, `CompilePassCustomDecorators` constructs the default compiler. * */ diff --git a/cpp/tests/association.cpp b/cpp/tests/association.cpp index ce5964e..df36c38 100644 --- a/cpp/tests/association.cpp +++ b/cpp/tests/association.cpp @@ -1,124 +1,138 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * association.cpp * * Author: pgess * Created on August 12, 2017, 9:28 PM */ #include "xreatemanager.h" #include "transcendlayer.h" #include "pass/interpretationpass.h" +#include "supplemental/docutils.h" #include using namespace xreate::interpretation; using namespace xreate; using namespace std; TEST(Association, TypedQuery_1) { auto man = ::xreate::details::tier1::XreateManager::prepare(R"Code( AtomNumT = type slave atomNumT. AtomStrT = type slave atomStrT. CompListT = type slave compListT. CompArithT = type slave compArithT. test = function:: num; entry { query1 = intrinsic query("atomNumT")::[AtomNumT]. query2 = intrinsic query("atomStrT")::[AtomStrT]. query3 = intrinsic query("compListT")::[CompListT]. query4 = intrinsic query("compArithT")::[CompArithT]. test1 = query1[0] == 5:: bool. test2 = query2[1] == "y":: bool. test3 = query3[0, 1] == "x" :: bool. test4 = query4[0, 0, 0] == 1:: bool. test1 + test2 + test3 + test4 } )Code"); man->transcend->addRawScript(R"RAW( atomNumT(5; 8). atomStrT("x"; "y"). compListT(5, "x"). compListT(8, "y"). compArithT(add(1, 2)). compArithT(mul(5, 6)). )RAW"); man->analyse(); int (*test)() = (int (*)())man->run(); int result = test(); ASSERT_EQ(4, result); } TEST(Association, QueryLate_ast_1){ auto man = XreateManager::prepare( R"Code( test = function:: num; entry { intrinsic query late("dict"->x:: bool)::bool { (x == true) } } )Code"); } TEST(Association, QueryLate_pass_1){ auto man = ::xreate::details::tier1::XreateManager::prepare( R"Code( test = function:: num; entry { intrinsic query late("dict"->x:: bool)::bool { (x == true) } } )Code"); man->analyse(); Expression instructionE = man->root->findFunction("test")->getEntryScope()->getBody(); InterpretationData instructionData = Attachments::get(instructionE); ASSERT_EQ(QUERY_LATE, instructionData.op); } TEST(Association, QueryLate_target_1){ auto man = ::xreate::details::tier1::XreateManager::prepare( R"Code( Equipment = type variant {dresser, sink, stove}. Room = type variant{kitchen, bedroom, bathroom}. test = function:: num; entry { room = bedroom() :: Room; room. equipment = intrinsic query late("whatshere"->x:: Equipment)::int; equipment { if (x == dresser()):: int {1} else {0} }. equipment + (room::int) } )Code"); man->transcend->addRawScript( R"RAW( room(kitchen; bedroom; bathroom). equipment(dresser;sink;stove). interior(kitchen, stove). interior(bedroom, dresser). interior(bathroom, sink). late(VarTarget, VarRoom, Room, whatshere(Equipment)):- bind(VarTarget, equipment); bind(VarRoom, room); interior(Room, Equipment); equipment(Equipment); room(Room). )RAW"); man->analyse(); int (*result)() = (int (*)()) man->run(); ASSERT_EQ(2, result()); } - - +TEST(Association, Doc_IntrinsicQuery_2){ + string scriptGUI = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "IntrinsicQuery_2_GUI"); + string scriptPlatform = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "IntrinsicQuery_2_Platform"); + string scriptRules = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "IntrinsicQuery_2_Rules"); + string code = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "IntrinsicQuery_2_Code"); + + auto man = xreate::details::tier1::XreateManager::prepare(move(code)); + man->transcend->addRawScript(move(scriptGUI)); + man->transcend->addRawScript(move(scriptPlatform)); + man->transcend->addRawScript(move(scriptRules)); + man->analyse(); + + delete man; + ASSERT_TRUE(true); +} diff --git a/cpp/tests/ast.cpp b/cpp/tests/ast.cpp index 2c5ce06..5072b32 100644 --- a/cpp/tests/ast.cpp +++ b/cpp/tests/ast.cpp @@ -1,204 +1,229 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * ast.cpp * * Created on: Jun 11, 2015 * Author: pgess */ #include "supplemental/docutils.h" #include "xreatemanager.h" #include "main/Parser.h" - +#include "supplemental/defines.h" #include "gtest/gtest.h" using namespace std; using namespace xreate; using namespace xreate::grammar::main; TEST(AST, Containers1) { FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r"); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Parser errors"); fclose(input); } TEST(AST, InterfacesDataCFA) { XreateManager* man = XreateManager::prepare ("interface(cfa){\n" " operator map :: annotation1.\n" "}"); auto answer = man->root->__interfacesData.equal_range(CFA); EXPECT_EQ(1, std::distance(answer.first, answer.second)); Expression&& scheme = move(answer.first->second); EXPECT_EQ(Operator::MAP, scheme.op); EXPECT_EQ("annotation1", scheme.getOperands().at(0).getValueString()); } TEST(AST, syntax_recognizeIdentifiers) { XreateManager* man = XreateManager::prepare(R"Code( test= function(a:: num):: num; entry { a = b:: int. b = 8:: int. a } )Code"); } TEST(AST, syntax_operatorIndex) { XreateManager* man = XreateManager::prepare(R"Code( test= function(a:: num):: num; entry { b = a[1]. b } )Code"); } TEST(AST, Variants_switch) { XreateManager* man = XreateManager::prepare(R"Code( Color = type variant{Blue, White, Green}. main = function:: int { x = White()::Color. switch variant(x)::int case (Green) {0} case (White) {1} case (Blue){2} } )Code"); Expression e = man->root->findFunction("main")->getEntryScope()->getBody(); ASSERT_EQ(4, e.getOperands().size()); ASSERT_EQ(3, e.blocks.size()); } TEST(AST, DISABLED_InterfacesDataDFA) { } TEST(AST, DISABLED_InterfacesDataExtern) { } TEST(AST, Doc_LiteralsAndExpressions) { XreateManager* man = XreateManager::prepare( R"Code( Record1 = type {year:: int, month:: string}. isOdd = function(x :: int) :: bool {true} test = function:: bool; entry { x1 = 5 :: int. x2 = "Nimefurahi kukujua":: string. x3 = {year = 1934, month = "april"}:: Record1. x4 = {16, 8, 3} :: [int]. + x41 = [1..18]:: [int]. x5 = 8>=3:: bool. x6 = "Blue" <> "Green" :: bool. + x7 = -true:: bool. colors = {"Green", "Blue"} :: [string]. color = colors[0] :: string. date = {year = 1934, month = "april"}:: Record1. year = date["year"] :: int. a = 0::int. b = 0 :: int. x7 = a - b:: int. result = isOdd(6) :: bool. true } )Code"); ASSERT_TRUE(true); } TEST(AST, Doc_CodeBlocks1) { XreateManager* man = XreateManager::prepare( getDocumentationExampleById("documentation/Syntax/syntax.xml", "CodeBlocks1")); - ASSERT_TRUE(true); + FnNoArgs resultFn = (FnNoArgs) man->run(); + int resultExpected = resultFn(); + ASSERT_EQ(12, resultExpected); } TEST(AST, Doc_Functions1) { XreateManager* man = XreateManager::prepare( getDocumentationExampleById("documentation/Syntax/syntax.xml", "Functions1")); ASSERT_TRUE(true); } TEST(AST, Doc_FunctionSpecializations1) { XreateManager* man = XreateManager::prepare( getDocumentationExampleById("documentation/Syntax/syntax.xml", "FunctionSpecialization1")); ASSERT_TRUE(true); } TEST(AST, Doc_BranchStatements) { string code_IfStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "IfStatement1"); string code_SwitchStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "SwitchStatement1"); string code = R"Code( test = function:: int; entry { question = "Favorite color?":: string. monthNum = 2:: int. %IfStatement1 %SwitchStatement1 monthName } )Code"; replace(code, "%IfStatement1", code_IfStatement1); replace(code, "%SwitchStatement1", code_SwitchStatement1); XreateManager* man = XreateManager::prepare(move(code)); ASSERT_TRUE(true); } TEST(AST, Doc_LoopStatements) { string code_LoopStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement1"); string code_LoopStatement2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "LoopStatement2"); string code_FoldStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "FoldStatement1"); string code_MapStatement1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "MapStatement1"); string code = R"Code( test = function:: int; entry { %LoopStatement1 %LoopStatement2 %FoldStatement1 %MapStatement1 min } )Code"; replace(code, "%LoopStatement1", code_LoopStatement1); replace(code, "%LoopStatement2", code_LoopStatement2); replace(code, "%FoldStatement1", code_FoldStatement1); replace(code, "%MapStatement1", code_MapStatement1); XreateManager::prepare(move(code)); ASSERT_TRUE(true); } TEST(AST, Doc_Types){ string code = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Types1"); XreateManager::prepare(move(code)); ASSERT_TRUE(true); } -TEST(AST, Doc_Variants){ +TEST(AST, Doc_Variants1){ string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Variants1"); XreateManager::prepare(move(code_Variants1)); + ASSERT_TRUE(true); +} + +TEST(AST, Doc_VariantsSwitch1){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "VariantsSwitch1"); + XreateManager::prepare(move(code_Variants1)); + + ASSERT_TRUE(true); +} + +TEST(AST, Doc_RecField1){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecField1"); + XreateManager::prepare(move(code_Variants1)); + + ASSERT_TRUE(true); +} + +TEST(AST, Doc_RecUpdate1){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecUpdate1"); + XreateManager::prepare(move(code_Variants1)); + ASSERT_TRUE(true); } \ No newline at end of file diff --git a/cpp/tests/compilation.cpp b/cpp/tests/compilation.cpp index 21c1d12..f098e2f 100644 --- a/cpp/tests/compilation.cpp +++ b/cpp/tests/compilation.cpp @@ -1,228 +1,243 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * compilation.cpp * * Created on: - * Author: pgess */ #include "xreatemanager.h" +#include "supplemental/defines.h" + #include "gtest/gtest.h" + using namespace xreate; //DEBT implement no pkgconfig ways to link libs //TOTEST FunctionUnit::compileInline TEST(Compilation, functionEntry1){ std::unique_ptr program(XreateManager::prepare( "func1 = function(a:: int):: int {a+8} \ func2 = function::int; entry {12 + func1(4)} \ ")); void* entryPtr = program->run(); int (*entry)() = (int (*)())(intptr_t)entryPtr; int answer = entry(); ASSERT_EQ(24, answer); } TEST(Compilation, full_IFStatementWithVariantType){ XreateManager* man = XreateManager::prepare( "Color = type variant {RED, BLUE, GREEN}.\n" "\n" " main = function(x::int):: bool; entry {\n" " color = if (x == 0 )::Color {RED()} else {BLUE()}.\n" " if (color == BLUE())::bool {true} else {false}\n" " }" ); bool (*main)(int) = (bool (*)(int)) man->run(); ASSERT_FALSE(main(0)); ASSERT_TRUE(main(1)); } TEST(Compilation, full_Variant1){ XreateManager* man = XreateManager::prepare(R"Code( OneArgument = type{x::int}. TwoArguments = type{x::int, y::int}. Command= type variant{ Add::TwoArguments, Dec::OneArgument }. main = function::Command; entry { Dec({x=2})::Command } )Code"); void (*main)() = (void (*)()) man->run(); } TEST(Compilation, full_SwitchVariant1){ XreateManager* man = XreateManager::prepare(R"Code( OneArgument = type{x::int}. TwoArguments = type{x::int, y::int}. Command= type variant{ Add::TwoArguments, Dec::OneArgument }. main = function::int; entry { //command = Dec({x = 8}):: Command. command = Add({x= 3, y= 5}):: Command. switch variant(command)::int case(Add){command["x"] + command["y"]} case(Dec){command["x"]} } )Code"); int (*mainFn)() = (int (*)()) man->run(); int result = mainFn(); ASSERT_EQ(8, result); } TEST(Compilation, full_SwitchVariantNoArguments2){ XreateManager* man = XreateManager::prepare(R"Code( Command= type variant{Add, Dec}. main = function::int; entry { command = Dec():: Command. switch variant(command)::int case(Add){0} case(Dec){1} } )Code"); int (*mainFn)() = (int (*)()) man->run(); int result = mainFn(); ASSERT_EQ(1, result); } TEST(Compilation, full_SwitchVariantMixedArguments3){ XreateManager* man = XreateManager::prepare(R"Code( TwoArguments = type{x::int, y::int}. Command= type variant{ Add::TwoArguments, Dec }. main = function(arg::int):: int; entry { command = if (arg > 0)::Command {Dec()} else {Add({x=1,y=2})}. switch variant(command)::int case(Add){0} case(Dec){1} } )Code"); int (*mainFn)(int) = (int (*)(int)) man->run(); int result = mainFn(5); ASSERT_EQ(1, result); } TEST(Compilation, full_StructUpdate){ XreateManager* man = XreateManager::prepare( R"Code( Rec = type { a :: int, b:: int }. test= function:: int; entry { a = {a = 18, b = 20}:: Rec. b = a + {a = 11}:: Rec. b["a"] } )Code"); int (*main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(11, result); } TEST(Compilation, AnonymousStruct_init_index){ std::string code = R"Code( main = function:: int; entry { x = {10, 15} :: {int, int}. x[1] } )Code"; std::unique_ptr man(XreateManager::prepare(move(code))); int (*main)() = (int (*)()) man->run(); EXPECT_EQ(15, main()); } TEST(Compilation, AnonymousStruct_init_update){ std::string code = R"Code( main = function:: int; entry { x = {10, 15} :: {int, int}. y = x + {6}:: {int, int}. y[0] } )Code"; std::unique_ptr man(XreateManager::prepare(move(code))); int (*main)() = (int (*)()) man->run(); EXPECT_EQ(6, main()); } TEST(Compilation, BugIncorrectScopes1){ std::string code = R"Code( init = function:: int {10} main = function(cmd:: int):: int; entry { x = init():: int. if(cmd > 0):: int { x + 1 } else { x } } )Code"; std::unique_ptr man(XreateManager::prepare(move(code))); int (*mainFn)(int) = (int (*)(int)) man->run(); EXPECT_EQ(11, mainFn(1)); } TEST(Compilation, Sequence1){ std::string code = R"Code( interface(extern-c){ libbsd = library:: pkgconfig("libbsd"). include { libbsd = {"bsd/stdlib.h", "string.h"} }. } start = function:: i32; entry { seq { nameNew = "TestingSequence":: string. setprogname(nameNew) } {strlen(getprogname())}::i32 } )Code"; std::unique_ptr man(XreateManager::prepare(move(code))); int (*startFn)() = (int (*)()) man->run(); int nameNewLen = startFn(); ASSERT_EQ(15, nameNewLen); } +TEST(Compilation, BoolInstructions1){ + std::string code = +R"Code( +test = function (a:: bool, b:: bool):: bool; entry +{ + -a +} +)Code"; + + std::unique_ptr man(XreateManager::prepare(move(code))); + Fn2args startFn = (Fn2args) man->run(); +} \ No newline at end of file diff --git a/cpp/tests/exploitation.cpp b/cpp/tests/exploitation.cpp index 217f1a4..8316438 100644 --- a/cpp/tests/exploitation.cpp +++ b/cpp/tests/exploitation.cpp @@ -1,38 +1,63 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * exploitation.cpp * * Author: pgess * Created on February 15, 2018, 6:17 PM */ #include "xreatemanager.h" #include "analysis/cfagraph.h" #include "pass/cfapass.h" #include "pass/cfatemporalseqpass.h" #include "supplemental/docutils.h" - +#include "supplemental/defines.h" #include "gtest/gtest.h" using namespace xreate; using namespace xreate::cfa; using namespace std; TEST(Exploitation, Doc_ResourceInit_1) { string example = getDocumentationExampleFromFile("scripts/exploitation/test1.xreate"); string body = getDocumentationExampleById("documentation/exploitation.xml", "ResourceInit_1"); replace(example, "", body); details::tier1::XreateManager* man = details::tier1::XreateManager::prepare(move(example)); CFATemporalSeqPass* pass = new CFATemporalSeqPass(man); man->registerPass(pass, PassId::CFATemporalSeqPass); man->analyse(); int (* main)() = (int (*)()) man->run(); int result = main(); delete pass; delete man; ASSERT_EQ(1, result); +} + +TEST(Exploitation, Doc_ExampleEov_1){ + string code = getDocumentationExampleFromFile("scripts/execution-order/test-1.xreate"); + string codeSpecializations = getDocumentationExampleById("documentation/index.xml", "ExampleEov_1_Specializations"); + + string script = getDocumentationExampleFromFile("/private/prg/code/xreate/scripts/execution-order/test-1.assembly.lp"); + string scriptExpectation = getDocumentationExampleById("documentation/index.xml", "ExampleEov_1_Expectation"); + string scriptRegistration = getDocumentationExampleById("documentation/index.xml", "ExampleEov_1_Registration"); + + replace(script, "", scriptExpectation); + replace(script, "", scriptRegistration); + replace(code, "", codeSpecializations); + + auto man = details::tier1::XreateManager::prepare(move(code)); + man->transcend->addRawScript(move(script)); + + man->analyse(); + FnNoArgs program = (FnNoArgs)man->run(); + delete man; + + testing::internal::CaptureStdout(); + program(); + std::string outputActual = testing::internal::GetCapturedStdout(); + ASSERT_NE(std::string::npos, outputActual.find("HeaderBodyFooter")); } \ No newline at end of file diff --git a/cpp/tests/transcend.cpp b/cpp/tests/transcend.cpp index 7795d83..022b105 100644 --- a/cpp/tests/transcend.cpp +++ b/cpp/tests/transcend.cpp @@ -1,58 +1,92 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Author: pgess * Created on June 7, 2018, 3:35 PM * * \file transcend.cpp * \brief Transcend's tests */ #include "xreatemanager.h" #include "transcendlayer.h" #include "supplemental/docutils.h" #include using namespace xreate; using namespace std; TEST(Transcend, Parse1) { std::string script = R"Code( )Code"; std::unique_ptr man(details::tier1::XreateManager::prepare(std::move(script))); std::string scriptTranscend = R"Code( test1((1)). test2((1, 2)). )Code"; man->transcend->addRawScript(move(scriptTranscend)); man->analyse(); StaticModel solution = man->transcend->query("test1"); Gringo::Symbol symbTest1 = solution.begin()->second; auto answer1 = man->transcend->parse>(symbTest1); ASSERT_EQ(1, get<0>(answer1).size()); solution = man->transcend->query("test2"); Gringo::Symbol symbTest2 = solution.begin()->second; auto answer2 = get<0>(man->transcend->parse>(symbTest2)); ASSERT_EQ(2, answer2.size()); } TEST(Transcend, Doc_Expressions1) { string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Expressions1"); + XreateManager* man = XreateManager::prepare(move(code)); + man->run(); + delete man; + ASSERT_TRUE(true); +} + +TEST(Transcend, Doc_SlaveTypes1){ + string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Codeblocks1"); XreateManager::prepare(move(code)); - ASSERT_TRUE(true); + ASSERT_TRUE(true); } TEST(Transcend, Doc_Codeblocks1) { string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Codeblocks1"); XreateManager::prepare(move(code)); ASSERT_TRUE(true); +} + +TEST(Transcend, Doc_Diagnostics1) { + string code = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Diagnostics1"); + string scriptTranscend = getDocumentationExampleById("documentation/Transcend/transcend.xml", "Diagnostics1_Rules"); + string scriptSupport = +R"Code( +scope_func_dict(S, Fn):- + cfa_parent(S, function(Fn)). + +scope_func_dict(S1, Fn):- + cfa_parent(S1, scope(S2)); + scope_func_dict(S2, Fn). +)Code"; + + auto man = XreateManager::prepare(move(code)); + man->transcend->addRawScript(move(scriptTranscend)); + man->transcend->addRawScript(move(scriptSupport)); + testing::internal::CaptureStdout(); + + man->run(); + delete man; + + std::string outputActual = testing::internal::GetCapturedStdout(); + string outputExpected = "warning(\"Visibility violation\",test,sum)"; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); } \ No newline at end of file diff --git a/documentation/Concepts/interpretation.xml b/documentation/Concepts/interpretation.xml index 50b46cb..e102841 100644 --- a/documentation/Concepts/interpretation.xml +++ b/documentation/Concepts/interpretation.xml @@ -1,638 +1,869 @@ <?xxe-sn 26cy4nalqsw pu35zrt1abya?>Interpretation Interpretation is a compiler's mode reserved to evaluate, expand and simplify parts of a given program based on information available in this mode. On the other hand, Interpretation is a middle man, or an intermediate level, between the Transcend and Brute levels, as it facilitates communication between those by means of interpreting data of respective layers. It can be further divided into Upper and Lower Interpretations depending on the interaction with the layer we are focused on at the moment. mode whereby - как это перевести? - это режим с помощью которого xreate интерпретирует? Авторский смысл: это режим(компилятора) В КОТОРОМ компилятор интерпретирует. Program: Речь идет о компьютерной программе, или может приложении для смартфона, или алгоритме? Тогда лучше употребить соответственно software ('piece of software') / app / algorithm. Вообще "наши" компьютерные программы - это у них software, а не programs - - ДА ТУТ СЛОВО PROGRAM ПОДХОДИТ, особенно в контексте обработки программы с какимито целями, как здесь, чтоб ее интерпретировать. name="tests/interpretation.cpp: Interpretation.Doc_Intr_1" main= function:: int; entry { x= "a":: string. y= if (x=="b"):: string; i12n(on) {1} else {0}. y } In this example, the identifier y has an attached annotation i12(on) which indicates that the compiler should use compile-time interpretation to evaluate y. Once the simplification process is over, the function returns 0, with neither memory allocations for the string variable x nor any computation at runtime. Annotation - может лучше value или parameter? - да Annotation подходит There are two annotations reserved to control the interpretation process: i12n(on) Forces compiler to interpret the annotated expression, function, or function argument. It yields error if the expression is impossible to interpret. i12n(off) Disables interpretation for the annotated expression.
<?xxe-sn 26cy4nalqsw pu35zrt1abyt?>Eligible Expressions Currently the compiler is able to interpret the following expressions: Atomic instructions: numbers, strings, identifiers. Relational and logic operators e.g. x==true, x!=0. if, switch statements. [[#late-interpretation-or-e|Statement expansion]] allowed. Loop statements. [[#late-interpretation-or-e|Statement expansion]] allowed. Functions. [[#function-interpretation|Function calls]], [[#partial-or-late-function|Partial function call interpretation]]. interpreta - это так и нужно, или неполное слово? - этот кусок текста на сайте превратится в ссылку, это адрес ссылки, такой должен быть Index operator e.g. x = {1, 2, 3}. y = x[0], info = {planet="Earth"}. x = info["planet"]. List operators e.g. x = [1..10]. y={"day", "night"}.
<?xxe-sn 26cy4nalqsw pu35zrt1abzi?>Function Interpretation The whole function body may be subject to interpretation if it consists of interpretable expressions only. name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_1" unwrap = function(data::unknType, keys::unknType):: unknType; i12n(on) { loop fold(keys->key::string, data->record):: unknType { record[key] } } test = function:: bool; entry { book = unwrap({ Library = { Shelf = { Book = "Aristotle's Politics" }}}, {"Library", "Shelf", "Book"}):: unknType. book == "Aristotle's Politics" } The above example demonstrates the unwrap function which is intended to be fully interpretable, as indicated by the function header's annotation. Obviously, the interpretable function requires that all its arguments are also interpretable. In this case the compiler is able to calculate the function's result at compile time with no byte code produced. Here is what the compiled code looks like (which will be also optimized out during the consequent compilation phases): name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_1" test = function:: bool; entry { book = "Aristotle's Politics":: string; i12n(on). book == "Aristotle's Politics" } The example also reveals a number of similarities with dynamically typed programming languages: number of similarities - тут точно нужен артикль, неопределенный (a) если в смысле "много схожестей", и определенный (the) - если речь идет о точном их количестве. a dynamically typed - неопределенный артикль никогда не употребляется при множественных объектах - OK Relaxed types. Notice unknType type which has not been defined. It's interpreted well because the compiler completely ignores the type system since everything can be checked at compile time anyway. The Interpretation mode is exactly the level where the relaxed type system is possible without any performance penalties or safety concerns. Introspection abilities. Notice how it is allowed to treat list fields as string keys, so functions like unwrap can get list field name as a parameter. Possible errors, such as the list not having the requested field are easily spotted by the compiler during interpretation and there are no concealed runtime bugs. In a sense, it is a fair trade off between introspection expressiveness and possible compilation errors. no hard to catch runtime bugs - не вполне ясно. В смысле, "легко заметить баги проявившиеся во время операции"? Тогда лучше "its is easy to see runtime bugs", или может "the runtime bugs are possible" (если смысл такой, что могут проявиться ошибки/баги"). It's и другие подобные сокращения: лучше употреблять полную форму - it is, так как it's это разговорный вариант - OK. Additional reason for the arbitrary undefined type unknType being used in the example is to ensure that no compilation occurs and everything is done at the interpretation mode. In simple cases interpretation analysis could determine that a function is subject to interpretation with no annotation hints provided. name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_2" unwrap = function(data::unknType, keys::unknType):: unknType { loop fold(keys->key::string, data->record):: unknType { record[key] } } test = function:: bool; entry { book = unwrap({ Library = { Shelf = { Book = "Aristotle's Politics" }}}, {"Library", "Shelf", "Book"}):: unknType; i12n(on). book == "Aristotle's Politics" } The only difference from the example above is the lack of annotation hint for unwrap. It can be seen that interpretation of the variable book is required which in its turn depends on unwrap. In this case analysis is capable enough to determine that unwrap is indeed possible to interpret, so no errors occur. "Developer requires interpretation" - тут речь о разработчике-программисте? Не вполне понятен смысл, поэтому пока оставляю как есть. - да программист, исправил There are, however, more complicated cases for interpretation analysis: Direct recursion. Interpretation analysis is able to correctly determine whether a function involving direct recursion (where the function calls itself) is subject to interpretation or not. Indirect recursion. Currently, when processing the indirect recursion (where a function is called not by itself but rather by another function), the analysis usually fails and relies on manually provided annotation hints. Below is an example of a direct recursion: name="tests/interpretation.cpp: Interpretation.Doc_FnIntr_3" unwrap = function(data:: X):: bool { if (data[0] == "the-end"):: bool {true} else {unwrap(data[0])} } entry = function:: bool; entry { unwrap({{{{"the-end"}}}}):: bool; i12n(on) } Function unwrap unwraps the nested list until the desired value is found. No function level annotation is required.
<?xxe-sn 26cy4nalqsw pu35zrt1ac0j?>Late Interpretation or Expansion Late Interpretation can be conceptualized as a partial expansion, i.e. a simplification or elimination of interpretable parts of certain statements. name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_1" test = function(x:: int):: int; entry { comm= "inc":: string; i12n(on). y= if (comm == "inc"):: int {x + 1} else {x}. y } In this example, computation of y depends on comm and x. On the one hand, comm has an annotation that requires interpretation, while on the other hand x is unknown at the compile-time and thus cannot be interpreted. In this case the only way to satisfy contradictory requirements is to expand the if statement, since it is only possible to interpret condition part of the statement, leaving conditional blocks unchanged. In other words, the if statement is expanded which results in only one of the child blocks being compiled, x+1 in this example, based on already known fact that the else block would never be executed. "to interpret condition part of the statement" - это не смог понять. - `if (...) {..} else {...}` is a statement. It consists of condition part `if (..)`, and two blocks - "if-true" block and "if-false" block Due to the fact that expansion, as opposed to "pure interpretation", leaves some portion of the code for subsequent compilation it can also be called late interpretation for the result depends on runtime information and has memory and performance footprint. as having runtime footprint - не понял. Что-то связанное с требованиями к памяти? Но это кажется о программах, а не о interpretation - переписал фразу Below is a more complex example of a loop expansion: name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_2" main = function(x:: int):: int; entry { commands = {"inc", "double", "dec"}:: [string]; i12n(on). loop fold(commands->comm::string, x->operand):: int { switch(comm):: int case ("inc") {operand + 1} case ("dec") {operand - 1} case ("double") {operand * 2} } } Identifier commands contains a list of operations that need to be interpreted as indicated by the corresponding annotation. Operand x is assigned at runtime. This is the same situation as in previous example, and it triggers expansion as expected. The result after expansion looks as follows: name="tests/interpretation.cpp: Interpretation.Doc_LateIntr_2" main = function(x:: int):: int; entry { x{1} = x + 1:: int. x{2} = x{1} * 2:: int. x{3} = x{2} * 1:: int. x{3} } In other words, this mimics the well known loop unrolling technique by putting several copies of the loop body in a row, each one for every item in the list commands. As of now, the following statements support late interpretation: Branching statements: if, switch, switch variant, switch late. Loop statements: loop fold. Functions. Other operators: query late.
<?xxe-sn 26cy4nalqsw pu35zrt1ac1n?>Partial or Late Function Interpretation Xreate supports cases where a function has mixed arguments in terms of interpretation, some of which need to be interpreted, while others need to be compiled. name="tests/interpretation.cpp: Interpretation.Doc_LateFnIntr_1" evaluate= function(argument:: num, code:: string; i12n(on)):: num { switch(code):: num case ("inc") {argument + 1} case ("dec") {argument - 1} case ("double") {argument * 2} case default {argument} } main = function(init::int):: int; entry { commands= {"inc", "double", "dec"}:: [string]; i12n(on). loop fold(commands->comm::string, init->operand):: int { evaluate(operand, comm) } } Looking at the function evaluate's signature in this example we can see only one argument code that requires interpretation. This means that the function evaluate is subject to a partial interpretation or, in other words, late function interpretation. In general, to enable late interpretation for a function, at least one of its arguments should be annotated as i12n(on). What compiler does next is to generate a number of distinctive function specializations. Each unique combination of interpretable argument values corresponds to its own function specialization. This should be used with cautiousness, since compiler can generate large amounts of code in some cases. What compiler does next is to generate number - то же самое, что в примеч. ниже (a number - несколько, the number - конкретное число).To enable late interpretation for function - тут перед function обязательно нужен артикль - определенный (the), если речь идет о evaluate, или неопределенный (а) если речь идет о любой функции (я указал the) - OK Based on the above example, three different evaluate specializations are generated as follows: name="tests/interpretation.cpp: Interpretation.Doc_LateFnIntr_1" main= function(init::int):: int; entry { operand = init:: int. operand{1} = evaluate1(operand):: int. operand{2} = evaluate2(operand{1})::int. operand{3} = evaluate3(operand{2})::int. operand(3) }
+
+ + + <?xxe-sn 2f6e093xhxc 2?>Transcend and Interpretation + + SYNTAX: +**intrinsic query** (//predicate//):: type; [//annotations-list//] + + + + + + + + predicate + Denotes a Transcend predicate. + + + + Represents the value of the Transcend + predicate predicate in the + format as follows: + + + + + + + + If a predicate has only one argument, + returns a list of elements of an appropriate type: int, + string, variant or tuple. + + + + + + in case of several arguments, the + arguments comprise a record. The function returns a list of + records. + + + + + + Predicates correspond to + variants. + + + + Interpretation can be viewed as the middle + ground between high level Transcend and low level Brute. In essence, the + principal role of Interpretation is to serve as a link between them by + interpreting data for Brute + from Transcend. The special built-in function intrinsic query + is provided to query data from Transcend allowing to process it + further. + + As a short example, assume that we have + Transcend facts as follows: + + person("Ben"). + + This data can be queried as below: + + Persons = type [string]. //we expect a list of strings +persons = function:: Persons +{ + intrinsic query("person"):: Persons +} + + The persons + function in this example returns a list of persons supplied by Transcend. + The example can be slightly rewritten using slave + types which are reserved to automatically identify an appropriate + type for the returned value. The next excerpt is equivalent to the + previous one: + + Person = type slave person. //equivalent to string +Persons = type [Person] + +persons = function:: Persons +{ + intrinsic query("person"):: Persons +} + + Querying allows to use powerful Transcend + capabilities to solve convoluted problems, consequently retrieving + reasoning solutions by Brute for efficient processing. Consider a more + complicated example dealing with a GUI. + + First, let us start off defining more or + less semantically an application's GUI on Transcend level: + + name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15 +%Layout consists of two blocks: +block(my_content; my_nav). + +%Assign roles to the blocks: +role(my_content, body). +role(my_nav, navigation). + +%Navigation block can be in either an iconized or expanded form: +form(none; expanded; iconized). +allowed_form(my_nav, (expanded; iconized)). + +%Visual theme: +background(my_content, color(blue)). +background(my_nav, color(grey)). + + Above we have described GUI consisting of + two blocks: main content and navigation blocks. The same application can + look differently depending on platform or viewport properties. Let us + define a platform: + + name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15 +% The height is greater than the viewport's width: +orientation(portrait). + + Having an application's semantic description + as well as a platform's description we can now combine them using + additional rules to produce the final result: how the application should + look like on the given platform. + + name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15 +% Portrait orientation rules: + +%Determine appopriate navigation list's form: +form(Nav, iconized):- + orientation(portrait); + allowed_form(Nav, iconized); + role(Nav, navigation). + +%Determine blocks' layout: +align(Nav, bottom_of(Body)):- + orientation(portrait); + role(Nav, navigation); + role(Body, body). + +% Landscape orientation rules: +form(Nav, expanded):- + orientation(landscape); + allowed_form(Nav, expanded); + role(Nav, navigation). + +align(Nav, left_of(Body)):- + orientation(landscape); + role(Nav, navigation); + role(Body, body). + + In short, the rules above read that we + place the expanded navigation block my_nav + and the content block my_content in + a row for wide displays; conversely, my_content + and iconized my_nav on top of each + other for narrow displays. + + After Transcend has decided actual forms + and blocks alignment, the next step is to retrieve solutions denoted by + the align and form + predicates and to actually draw GUI elements: + + name="tests/association.cpp: Association.Doc_IntrinsicQuery_2", lines=15 +//Define types: +Block = type slave block. +Role = type variant {body, navigation}. +Alignment = type variant{ + bottom_of:: Block, + left_of::Block +}. +Layout = type {Role, Alignment}. + +//Determine role and form: +drawBlock = function(block:: Block):: Drawing +{ + forms = intrinsic query ("form"):: [Form]. + formCurrent = getBlockForm(forms, block):: Form. + + roles = intrinsic query ("role"):: [Role]. + roleCurrent = getBlockRole(block):: Role. + + switch variant (roleCurrent):: Drawing + case (body) + {drawBody(formCurrent)} + + case (navigation) + {drawNavigation(formCurrent)} +} + +//Determine layout +draw = function:: Drawing; entry +{ + layout = intrinsic query ("layout"):: Layout. + blockA = layout[0]:: Block. + + switch variant (layout[1]->blockB):: Drawing + case (bottom_of) + {drawVerical(blockA, blockB)} + + case (left_of) + {drawHorizontal(blockA, blockB)} +} + + Notice that the draw + and drawBlock functions work with + compile time data from Transcend, thus all this code after interpretation + gets simplified into the following: + + drawVertical(drawBody(none()), drawNavigation(iconized())). + + The example above demonstrates the + possibility to delegate to Transcend intelligent + tasks, such as adapting a GUI to a particular platform, and + leaving Brute with efficient implementation matters. +
+
<?xxe-sn 26cy4nalqsw pu35zrt1ac21?>Domain Specific Languages and Interpretation DSL is an idea of expressing various concepts in a lapidary and concise form. Xreate recognizes and acknowledges very successful and beneficial DSL usage in certain areas, primarily to express queries and configs, to name a few. It is possible to use interpretation abilities to emulate DSL. Developer can express the desired functionality in the form of nested lists of numbers, variants and strings which are then processed by a partially interpreted function. Such function in its turn transforms the input data into a set of low level compilation instructions so there is no runtime overhead.
<?xxe-sn 26cy4nalqsw pu35zrt1ac26?>On Interpretation Analysis Analysis follows classical type reconstruction algorithms to determine which expressions are subject to interpretation and check the correctness of reconstruction w.r.t. developer-provided annotations. Analysis consists of two general parts: Inference. Infers if it is possible to interpret an expression based on its already inferred argument's decisions. Unification. Assigns an appropriate decision w.r.t. previously inferred expectations and developer-provided hints as well.
\ No newline at end of file diff --git a/documentation/Syntax/syntax.xml b/documentation/Syntax/syntax.xml index 2f3e370..c079908 100644 --- a/documentation/Syntax/syntax.xml +++ b/documentation/Syntax/syntax.xml @@ -1,959 +1,1232 @@ <?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: + There is a number of principles the Xreate + syntax is based on: - Follows SSA form: each identifier is - defined only once and no redefinitions allowed + Follows SSA(single assignment) 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. + Follows literate programming principles + where possible. For example, identifiers can be defined in any order + reflecting personal preferences and convenience for a developer. + Regardless of the definition order, expressions are computed based on + dependencies between them. Literate programming principles from a + practical point of view simplify code testing, support, catching + regressions, etc.
- <?xxe-sn 26yv439af40 62?>Literals and expressions + <?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" + numbers, strings: 5, + "Nimefurahi kukujua". Lists, records - Record is a collection of elements - of different types — {year = 1934, - month = "april"}. List is a collection of elements of the - same type without keys — {16, 8, - 3}. Range of elements — [1..18]. + Record + is a collection of elements of different types - {year = 1934, month = "april"}. + List is a collection + of elements of the same type without keys - {16, 8, 3}. + Range is a specific form of a list - [1..18]. Arithmetic operations Basic arithmetic operations: - +, -, *, / + +, -, + *, /. + + + + + + Boolean + operations + + Negation example: + -isEmty(). Relations - ==, - !=, <>, <, <=, >, >=. Both - !=, <> mean - not equal relation. - Examples: 8>=3, "Blue" <> - "Green" + ==, + !=, <>, + <, <=, + >, >=. + Both !=, <> + mean not equal + relation. Examples: 8>=3, + "Blue" <> "Green". - List and struct operations + List and record 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"] + The index + operation to access individual elements of a list or record. + Example: colors = {"Green", "Blue"}::[string]. color = colors[0]:: string. + Accesing a record's element: date = {year = 1934, month = "april"}. year = date["year"]. Identifiers - Example: a - - b + Example: a - b Functions - Example: result - = isOdd(6). + Example: result = isOdd(6):: bool.
+
+ + + <?xxe-sn 2evpzktyvg7 -wunr7fl0rw8u?>Annotations + + This chapter is about Brute + syntax. See Transcend + for details regarding Transcend and annotations syntax. +
+
<?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. + Code block is a list of expressions + delimited by a period. It has a body + - the main expression along with optional identifiers' definitions. SYNTAX: { - [//ident// = //expression// . | //body-expression// . ].. + [//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. + Code blocks consist of a + body expression and an optional set of assignments to define + identifiers used in the body expression. A code block's computation result + is defined as a result of computing its body expression. Identifiers are + computed before expressions they are appear in, regardless of a + definitions' order. name="tests/ast.cpp: AST.Doc_CodeBlocks1" -test = function:: int +test = function:: int; entry { + a + b:: 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. + Above is an example of the code block with + a+b as its body expression(because + it does not have a form of an assignment). In this case the body depends + on the identifiers a, + b so the compiler computes both of + them beforehand. - Computation order depends only on + A computation order depends only on dependencies between expressions. This approach has properties as follows: Mutually independent identifiers can be - evaluated in any order + evaluated in any order. - Identifier gets computed only if it's - required(even transitively) by block's body expression. + An identifier computed only if it is + required(at least transitively) by a code block's body + expression.
<?xxe-sn 26yv439af40 7u?>Functions SYNTAX: -//function-name// = **function** ([//argument//:: //type//[; //annotation-list//]]...):: //return-type// [; //annotations//]... +//function-name// **=** **function** **(**[//argument//:: //type//[; //annotation-list//]]...**)::** //return-type// [; //annotations//]... //function-block// function-name - name of function + Name of a function. argument - formal parameter. Arguments are delimited by comma. + Formal parameters. Arguments are delimited by comma. type, - return-type formal - parameter and returning value types + return-type Formal + parameter's and returning value's types. function-block - code block that acts as a function's definition + Code block that acts as a function's definition. annotations - list of annotations delimited by semicolon + 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. + Below is an example of the function + sum returning sum of its two + arguments. Moreover there are several annotations defined. First + annotation entry has a special + meaning — it depicts an entry point or a main function in a program. + Second annotation status(needs_review) + is a demonstration that developers can annotate functions using custom + annotations to express related intentions or properties. name="tests/ast.cpp: AST.Doc_Functions1" sum = function(x:: int, y:: int):: int; entry; status(needs_review) { - x+y + x + y }
<?xxe-sn 26yv439af40 8j?>Function Specializations SYNTAX: -**guard**:: //annotation// +**guard::** //annotation// { //functions-list// } annotation - Guard expressed by annotation + Guard expressed in the form of an annotation. functions-list - one or more function that share the same guard + One or more functions 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. + Specializations is a crucial Xreate concept + serving as the principal connection between Transcend and Brute levels. + Xreate allows several functions to share the same name; in this case they + are called specializations. + This is a syntactic foundation for function + level polymorphism, + i.e. ability for the compiler to decide which exactly function is called + out of several available options. The polymorphism resolution can happen + during compilation(early polymorphism) or at run-time(late + polymorphism). 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. + different specializations must have identifiers called guards + to uniquely define a specialization. In this sense, a shared name is just + a placeholder, which only along with a guard comprise the fully qualified + exact function identifier. On the Brute level it is possible to specify + only a function's shared name for invocation. On the other hand, Transcend + is responsible to supply a guard part. When a function is actually called + by its name in a program it's expected that the resolution is already done + by Transcend at some time earlier and it supplies the correct guard to + uniquely specify which exactly specialization to call. This gives + Transcend a deep control over an actual program behaviour. + + An example: + + name="tests/ast.cpp: AST.Doc_FunctionSpecializations1", lines=15 +guard:: safe_enviroment +{ + sum = function (a::int, b::int):: int + { + result = a + b:: int. - Example: + if (-isLastOpOverflow(result)):: int + { + result + } + else + { + overflowErrorCode() + } + } +} - name="tests/ast.cpp: AST.Doc_FunctionSpecializations1" -guard:: safe_enviroment +guard:: fast_enviroment { sum = function (a::int, b::int) :: int { - a + b + a + b } } + To alter existing code behaviour + it's always possible to add new specializations and adjust Transcend rules + to specify situations when a new specialization should(or should not) be + used. + See API - to get more information on how guards are processed + to get more detailed information on how guards are processed on the + Transcend side.
<?xxe-sn 26yv439af40 93?>Branch Statements
<?xxe-sn 26yv439af40 95?>IF Statement SYNTAX: -**if** (//condition//):: //type// [; //annotations// ].. +**if (** //condition// **)::** //type// [; //annotations// ].. //block-true// **else** //block-false// - IF + The if statement executes block-true or block-false depending - on condition evaluation - result. + on the condition + evaluation's 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].. +**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 + condition + Expression to to decide which branch to execute next. guard - value to match against condition + Value to match against condition. default-code-block - executed if no appropriate case found + Executed if no appropriate case found. - SWITCH statement evaluation's result is - that of branch whose guard - matches condition. + The switch + statement evaluation's result is that of the branch whose + guard matches the + condition. - Example: + An 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"}. + case default {"It's strange..an unexpected month"}.
- <?xxe-sn 26yv439af40 9x?>Loop Statements + <?xxe-sn 26yv439af40 9x?>Loops + + Xreate loops are constructed in + such a way that they hide actually mutable operations semantic under an + immutable facade compatible with a single assignment form.
<?xxe-sn 26yv439af40 9z?>LOOP Statement SYNTAX: -**loop** ( //init-value// -> //accumulator// ):: //type// [; //annotations//] //loop-body// +**loop (** //init-value// **->** //accumulator// **)::** //type// [; //annotations//] //loop-body// init-value - initial value loop starts from + Initial value a loop starts from. accumulator - identifier which holds loop's result after each iteration + 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. + assumes the result of a previous iteration or init-value + for the first iteration. The result of loop-body + evaluation is used as a accumulator's + next iteration value and as an 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: + Note, that this notation does not have an + explicit termination condition! The compiler relies on the loop body's + fixed point in order to decide when to interrupt the loop. Consider an + example: COUNTEREXAMPLE, name="tests/ast.cpp: AST.Doc_LoopStatements" -//infinite loop -answer = loop (2->x) :: int +//an 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: + perfect(sum of all proper divisors equals to the number itself). During + iterations the accumulator x + assumes values as follows: 2, 3, 4, 5, 6, 6... After the first perfect + number is found, no further iteration will change the result anymore + since there is no increment, so the loop continues to go through the + same number again and again, making this an infinite loop. Obviously, + x=6(the first perfect number) is a + fixed point in this example. It does not make any sense to continue + going through further iterations once a fixed point is reached because + the result is not going to be changed anymore, thus the loop can be + safely interrupted at this point. + + The compiler relies on manually provided + annotations to recognize when a fixed point is reached. There is a + special annotation final reserved + to specify a fixed point for loops. Once an expression that marked as + final gets evaluated it's assumed + that a fixed point is reached or in words the compiler knows it's the + very last iteration after which loop can be terminated safely. The + correct code for the example above is: name="tests/ast.cpp: AST.Doc_LoopStatements" -//loop exits after first perfect number is found +//a loop exits after the 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. + In this case the compiler is able to + recognize that a fixed point is reached in order to know when it is safe + to terminate the loop. In the example, the final result answer2 + is 6.
<?xxe-sn 26yv439af40 aq?>LOOP FOLD Statement SYNTAX: -**loop fold** (//list// **->** //element//:: //type// [; //annotations//], //init-value// **->** //accumulator//):: //type// [; //annotations//] +**loop fold (** //list// **->** //element//:: //type// [; //annotations//], //init-value// **->** //accumulator//**)**:: //type// [; //annotations//] //loop-body// list - to iterate through + Container to iterate over. element - identifier that assumes value of currently processed list - element + Identifier that assumes value of a currently processing list + element. type, - annotations expression - types and optional annotations delimited by semicolon + annotations Expression + types and optional annotations delimited by semicolon. init-value - accumulator's initial value loop starts with + Accumulator's initial value loop starts from. accumulator - identifier assumes loop-body evaluation result after each - iteration + Identifier that assumes loop-body evaluation result after each + iteration. - Iterates over list - in order to accumulate result by applying loop-body + The loop fold + statement is a commonly used particular instance of loop + to Iterate over list in + order to accumulate the result by applying the 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. + and an intermediate accumulator. + The result of a current iteration is used as the accumulator + value for a next iteration. Accordingly, the overall loop value equals + that of accumulator + after the last iteration. If a fixed point is found evaluation + terminates earlier. - Example shows code excerpt that looks for - a minimal element in the given list(and less then initial value - 10). + Example shows a code excerpt that looks + for the minimal element in a given list(and less then the 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 map (**//list// **->** //element// :: //type// [; //annotations// ] ) :: //type// [; //annotations// ] //loop-body// list - to iterate through + Container to iterate over. element - identifier that assumes value of currently processed list - element + Identifier that assumes value of a currently processed list + element. type, - annotations type and + 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. + The loop fold + statement is a commonly used particular instance of loop + to Iterate over list and + applying the loop-body + transformation to each element. The result is a list that consists of + all the transformed elements. + + An example below demonstrates + creating the even_number + list by multiplying by 2 every element of odd_numbers: name="tests/ast.cpp: AST.Doc_LoopStatements" odd_numbers = {1, 3, 5}:: [int]. -even_numbers = loop map(odd_numbers->number::int) :: [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 + Primitive Types: - - - num - - i32 - alias. Reserved as a placeholder for auto detected appropriate - either integral of floating-point type - - - - + - int + bool - i32 - alias. Reserved as a placeholder for auto detected appropriate - integral type + Booleans. - + - float + i8, + i32, i64 - Double precision floating-point - number + Signed integers; 8, 32, 64 bit wide + respectively. - + - bool + int, + num - Boolean type + Currently i32 + aliases. Reserved as placeholders for an auto detected appropriate + integral type and for auto detected appropriate either integral of + floating-point type, respectively. - + - i8, - i32, i64 + float - Signed integers. 8, 32, 64 bit - wide respectively + Double precision floating-point + numbers. string - Pointer to a null terminated ANSI - char string. Reserved for auto detected most appropriate string - type. + Currently null terminated ANSI + char string. Reserved to be generic type with no particular + implementation. A concrete implementation is to be determined + similarly to the containers + approach. * - Unspecified type. Example - x = {amount=200, - currency="USD"}::*. + An unspecified type. Postpones + type checks for this expression. Examples: x = {amount=200, currency="USD"}::*. Compound types: - [ - element-type - ] + element-type ] List of elements of the same type element-type. - Example: [int] - list of - int's + Examples: x = {1, 2, 3}:: [int] + - list of int's. Lists can + have different internal implementations. - {key:: - type, - ...} + { key:: type, ... } - List of elements of different type - possibly with named keys. Examples: {int, - string}, {name::string, - age::int} + Record: a list of elements of + different types possibly with named keys. Examples: + {int, string}, + {name::string, age::int}. - variant - {option :: - (type, ...}, - ...} + variant {option :: {type, ...}, ...} - Holds a single element of type of - one out of number of options. Examples: variant - {FullAddress:: {string, string, string}, ShortAddress:: - {string}} + ADT type. Variables of this type + can hold value of any type out of a list of permitted ones. + Examples: variant {FullAddress:: {string, string, string}, ShortAddress:: {string}}. - slave - identifier + slave identifier + + Denotes a type constructed by + Transcend. See slave + types. An example: slave unit_test. + + + + + + Type operations: + + + + + + + + + + + + + + + + + + type [ key ] - Type determined by Transcend. - Example: slave - unit_test + Index operation: accessing elements + of a compound type. Examples: Bio = type {birth_year:: int, name:: string}. YearBirth = type Bio[birth_year]. - + - compound-type - [ key - ] + type ( parameters... ) - Accessing elements of compound - type. Example: Bio = type - {birth_year:: int, name:: string}. Year = type - Bio[birth_year]. + Constructs a concrete type with + the given parameters. Examples: MyTree = type Tree(int). - New types defined as: + New types are defined as follows: SYNTAX: //type-name// = **type** (//parameters//...) //type-definition// . - Example: + Examples: name="tests/ast.cpp: AST.Doc_Types" Tuple = type {string, int}. -Int = type Tuple[1]. //accessing by index +Int = type Tuple[1]. //accessing by index + +List = type(X) [X]. // List of elements of type X. +IntList = type List(int). // type function to construct List of ints. + +
+ + + <?xxe-sn 2f0cduzin5j -wunr7fl0rw8u?>Slave Types + + SYNTAX: +**slave** //predicate// + + + + + + + + + predicate + Name of a logic predicate + + + + Slave type is a reference to a + type defined on the Transcend side. This gives Transcend a full control + over program types marked as slave + ones. The type is constructed in such a way that variables of this type + are able to hold predicate's + arguments. Type inference works as follows: + + + + + + + + If a predicate has only + one argument then a constructed type is a type of this argument: + int, + string, variant or + tuple. + + + + + + A constructed type is a + record in case of several arguments. + + + + + + Predicates correspond to + variants in a constructed type. + + + + An example; Transcend + facts: + + person("John", 1962). +person("Bill", 1961). + + The Brute side: + + name="tests/transcend.cpp: Transcend.Doc_SlaveTypes1", lines=15 +PersonNative = type {string, int}. +Person = type slave person. + + In the example above the types + PersonNative and + Person are + equivalent. +
+
+ +
+ + + <?xxe-sn 2ey6qxf8um8 1a?>Variants + + Sometimes it is useful for a variable to + have an ability to hold values of different types depending on some + conditions, in other words to have a variant + type. An example: + + name="tests/ast.cpp: AST.Doc_Variants1" +Color = type variant { + White, Black, Magenta, + CustomColor:: {r:: int, g:: int, b:: int} +}. + +draw = function:: int +{ + clrBorder = Black():: Color. + clrBackground = CustomColor(50, 50, 50):: Color. + + drawRectangle({0, 0, 100, 100}, clrBorder, clrBackground) +}
- <?xxe-sn 26yv439af40 ej?>Variants and SWITCH VARIANT - Instruction + <?xxe-sn 26yv439af40 ej?>SWITCH VARIANT Statement SYNTAX: -**switch variant** ( //condition// [-> //alias// ] [:: //type// [; //annotations//... ] ] ) :: type [; annotations... ] +**switch variant** ( //condition// [**->** //alias// ] [:: //type// [; //annotations//... ] ] ) :: type [; annotations... ] [ **case** ( //guard// ) //case-branch// ]... condition - expression of variant type + Expression of a variant type. alias - identifier to denote unwrapped content of condition withing case - branches. + Identifier to denote unwrapped content of the condition + expression withing case branches. guard - name of variant to match against actual condition's variant + Name of a 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 . + Code block to execute in case of the matched variant. The + condition expression's + content is referred to by 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. + Variant variables require special means to + test which exactly variant they contain at any given moment as well as to + access it. Usually, languages that support variant types(ADT) solve this + problem be means of pattern + matching. Xreate intentionally does not support pattern + matching since it is depends on parameters order, which is plainly + unacceptable; besides, it's hardly usable in case of a large amount of + parameters. Instead, Xreate supports special syntax to unwrap + a variable's content using switch variant + statement. - Let's consider example with variable - month of variant type - Month: + An example: - name="tests/ast.cpp: AST.Doc_Variants" + name="tests/ast.cpp: AST.Doc_VariantsSwitch1",lines=15 Month = type variant { - MonName :: {string}, - MonNumber:: {int} + MonByName :: {name:: string}, + MonById :: {id:: int} +}. + +nextMonth = function(month:: Month):: Month +{ + switch variant(month):: Month + case (MonByName) + { + monthName = month["name"]:: string. //here month has {name:: string} type + + MonByName(nextMonthByName(monthName)) + } + + case (MonById) + { + monthId = month["id"]:: int. //here month has {id:: int} type + + if(monthId == 11):: Month + { MonById(0) } + else + {MonById(monthId + 1)} + } +} + + The function nextMonth + computes the next month after a given one. The parameter month + can't be directly accessed due to being of a variant type; hence, It + should be unwrapped before + using. As seen in this example, Xreate silently defines a new variable + with the same name month which holds + an unwrapped content for each switch variant's + branch independently. +
+ +
+ + + <?xxe-sn 2ey6qxf8um8 1u?>Records + + Record's elements(fields) are denoted by + strings in order to access the field's value. This gives a possibility to + use variables to reference fields. Such references are statically resolved + during Interpretation. + An example: + + name="tests/ast.cpp: AST.Doc_RecField1" +Employee = type { + name :: string, + surname :: string, + signature:: string }. -test = function:: Month +test = function:: string; entry { - month = MonName("April"):: Month. - month + employee = getAnyEmployee():: Employee. + primaryKey = "surname":: string. + + employee[primaryKey] } - 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. + In Xreate the left side of any assignment + is always an identifier, hence there is special syntax to update one(or + more) record's fields. An example: - As an example below is function - nextMonth's definition: + name="tests/ast.cpp: AST.Doc_RecUpdate1" +Day = type { + year:: int, + month:: string, + day:: int +}. - nextMonth = function(month:: Month):: Month +test = function:: Day { - switch variant(month):: Month - case (MonName) - { - // - } - case (MonNumber) + tomorrow + + today = {year = 1936, month = "July", day = 21}:: Day. + tomorrow = today + {day = 22}:: Day. }
+?> \ No newline at end of file diff --git a/documentation/Transcend/transcend.xml b/documentation/Transcend/transcend.xml index 9ca00af..26d6895 100644 --- a/documentation/Transcend/transcend.xml +++ b/documentation/Transcend/transcend.xml @@ -1,443 +1,578 @@ <?xxe-sn 2ajela3ur5s 7r?>Transcend - Transcend is a compilation phase and process - of reasoning about program in order to influence compilation. + Transcend is a compilation phase and a + process of reasoning about a program in order to influence final compilation + results. + + First, the compiler extracts + annotations from a source code + as well as other logic facts + from a number of different sources. Second, logic facts are coupled with + logic rules, that describe how + to process input data, to form a complete logic + program. Finally, a solver processes the logic program and + produces decisions that affect and control compilation in a number of ways. + Crucial difference of annotations from language keywords is that they do not + have any predefined meaning and semantic; only custom developer provided + rules make them matter. + + Currently, + an external logic solver Clasp is used to perform Transcend tasks. Hence, + logic rules are written(and annotations are converted to) in the ASP format: + one of standard first order logic notations. - 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. +
+ + + <?xxe-sn 2f0cduzin5j -wunr7fl0rw84?>Reasoning Solutions + + Transcend prepares solutions to + verify, control and guide compilation process. For each uncertain + situation the compiler consults reasoning solutions to know how to + proceed. Thus, many compilation aspects are delegated to Transcend and are + ultimately exposed to a software developer. Transcend solution are used in + a number of ways as follows: + + + + + + + + Direct + connection via the query + built-in function. A program can request and explicitly process + received data from Transcend as it sees fit. + + + + + + Function + level polymorphism. This is the principal way for Transcend to + interfere in compilation. Polymorphism is based on a possibility for a + function to have several specializations. Each time the function is + invoked in a client code, Transcend decides which exactly + specialization to call. This gives a deep control over final program + behaviour. + + + + + + Module + level polymorphism. Transcend decides which modules to choose + that satisfy a client code's requirements. + + + + + + The compiler allows + Transcend to add hidden parameters every time specific functions are + invoked. This is implemented to support the Context + concept. + + + + + + Diagnostics. Transcend is + able to validate different aspects of a program and print diagnostic + messages to signal found inconsistencies. + + + + As an example, suppose we want + to control functions visibility and want to separate the visibility(private) + functions from the rest of a program: + + name="tests/transcend.cpp: Transcend.Doc_Diagnostics1" +sum = function(a:: int, b:: int) :: int; visibility(private) +{ + a + b +} + +calculate = function(a:: int, b:: int):: int; visibility(pub_interface) +{ + sum(a, b) +} + +test = function(a:: int, b:: int) :: int; entry +{ + sum(a, b) // we may not invoke this directly +} + + Transcend converts the code + above to logic facts as follows(the relevant part is shown): + + function(sum) +function(calculate) +function(test) + +bind_func(sum,visibility(private)) +bind_func(calculate,visibility(pub_interface)) +bind_func(test,entry) + +cfa_parent(0,function(sum)) +cfa_parent(1,function(calculate)) +cfa_parent(2,function(test)) + +cfa_call(1,sum) +cfa_call(2,calculate) + + At this point all the + annotations are completely unknown to the compiler. Next, we need to write + rules to check that the visibility(private) + functions are called either by another visibility(private) + ones or by the visibility(pub_interface) + functions which are intended to act as a contact point, the only allowed + gate between the private part and the rest of a program: + + name="tests/transcend.cpp: Transcend.Doc_Diagnostics1" +% Define which functiones are allowed to invoke private ones +allowed( + visibility(private); + visibility(pub_interface) +). + +% Check every invocation for violations: +warning("Visibility violation", FuncFrom, FuncTo):- + cfa_call(ScopeFrom, FuncTo); + bind_func(FuncTo, visibility(private)); + scope_func_dict(ScopeFrom, FuncFrom); + { bind_func(FuncFrom, Visibility): allowed(Visibility) } 0. + + Given the rules Transcend goes through all + the invocations to check are there any possible visibility violations. For + this example Transcend generates a diagnostic message: warning("Visibility violation",test,sum). + + This way it is possible to validate and + guide compilation process by analysing program properties employing + provided processing rules. +
<?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: + supplementary information to be processed by the compiler along with a + source code proper with an end goal being to affect a compilation process. + Annotations are special expressions in the following form: Literals Strings, numbers. Example: 5, "success" Predicates Predicates have zero or more arguments. Example: final, status(broken), version("12.0.3", unstable) Negation Example: -i12n, -access(allowed) Various Xreate's entities can be annotated. At the time following entities are supported: Expressions - and Identifiers + xlink:href="#expressions">Expressions Code blocks and context Functions and Function Guards Modules
- <?xxe-sn 2ajela3ur5s 8v?>Expression and Identifiers + <?xxe-sn 2ajela3ur5s 8v?>Expressions SYNTAX: //expression// **::** //type//**;** //annotation-list//. type - expression's type + The expression's type annotation-list - one or more annotation attached to the expression. Annotations are - delimited by semicolon + one or more annotations attached to the expression. Annotations are + delimited by semicolon. - Example: + An example: name="tests/transcend.cpp: Transcend.Doc_Expressions1" -test = function:: int +test = function:: int; entry { - 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 = 5:: int; arithmetic(fast). //an annotation applied to an identifier + y = ((x - 8):: int; arithm_optimization(off)) * 2:: int. //an annotation applied to a nested expression x+y } + For readability's sake compound + statements(e.g. loops) + have slightly different syntax for 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 + A current limitation is that + expression's annotations are always preceded by a type, hence to + annotate nested expressions it is necessary to duplicate the + expression's type.
<?xxe-sn 2ajela3ur5s 9b?>Code Blocks and Context SYNTAX: **context** :: //annotations-list//. annotations-list - List of annotation delimited by semicolon + A list of annotations delimited by semicolon - Code block annotations called - context. Keyword - context used to declare annotation - within enclosing code block. + Code block annotations are called + context. The keyword + context is reserved to declare + annotations within an enclosing code block. - Example: + An example: name="tests/transcend.cpp: Transcend.Doc_Codeblocks1" test = function:: int { context:: arithm_optimization(off). x = 10 :: int. 2 * x - 16 }
<?xxe-sn 2ajela3ur5s 9n?>Special Annotations - There are some annotations in Xreate that - have special meaning + Despite of "annotations do not have any + predefined meaning" being a rule of thumb, still there are deviations in + Xreate; few annotation that have special meaning are: - + - + entry - Specifies entry point of a + Specifies the entry point of a program. See Function - Syntax + Syntax. final - Specifies fixed point of loop - statements. See Loop - Statement + Specifies the fixed point of + loops. See Loops.
<?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 + Annotations is a mechanism to express + additional pieces of information and can occur in a 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: + such as: identifiers and expressions, statements, code blocks, functions, + modules or even a whole program. + + Annotations are explicit + facts about a source code provided by a code writer in an + explicit form. Beside annotations, there are other sources of information, + in other words, implicit + facts that are automatically inferred by various analysis + phases to express useful insights regarding an analysed code. Such pieces + aren't exposed directly to a developer but accessed and used internally at + the reasoning phase along with annotations. All types of input are summed + up below: - Explicit annotations in source - files + Explicit annotations - Annotations provided by developer - in order to explicitly affect compilation + Annotations provided by a code + writer in order to explicitly affect compilation. - Code Analysis + Code analysis - Different code analyses during - compilation provide information implicitly inferred from source - code + Different code analyses provide + information implicitly inferred from a 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 + External facts that reflect a + hardware and/or an environment where compilation takes place or + where a program is supposed to work. There are many supervision + layers possible, from custom compiler's mode, to OS- or computer- + or even network-wide levels. In other words, supervision covers + local or client supplied annotations, e.g. prohibits web cam + usage, or external network resources access. Audit Third party supplied external - information that reflect additional software properties, e.g. - results of security audit + information that reflects additional software properties, e.g. + results of security audit. Reasoning rules Reasoning infers additional facts - from pool of previously gathered information. It is done by using - resoning rules that - govern reasoning process + from a pool of previously gathered information. It is done by + using resoning rules + that govern reasoning process.
<?xxe-sn 2ajela3ur5s av?>Why Annotations Matter This section goes through differences that annotations have over traditional statements.
<?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. - - - - - Yet there are several reserved - annotations that have specific meaning in Xreate. See special - annotations for details. - + syntax and there is no predefined set of allowed annotations. A + developer is free to define and use custom annotations to express + desired properties without prior changes to the compiler.
<?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. + comprise only an input for a reasoning procedure that ultimately decides + how exactly to influence compilation.
<?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. + sources enabling annotations to complement each other. The compiler + blends in annotations originated from various sources seamlessly to + improve decisions quality.
<?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. + Annotations can be used not only to + express properties of a program itself but also properties of a local + environment(OS, computer, local network) where the program is supposed + to run. Thus, in fact, the compiler operates by information that is + usually inaccessible during software development and can't be reflected + in a program code otherwise. In essence, it is postcompilation + - a process of adjustments to a particular environment and functioning + mode.
<?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. + strictly defined hierarchical structure of interaction between + components. For example, let us focus on a client-service model. A + service works the same regardless of a particular client in order to be + compatible with as wide set of different clients as possible. However + clients may use only small subset of service functionality or use it in + a specific and predictable order. Generally speaking, the service in + case of having additional information about a particular client is able + to serve requests more efficiently in many situations. It can be conceptualized as a feedback from a client. - Annotations is good candidate to express feedback to improve interaction - quality. + Annotations is a good candidate to express feedback to improve + interaction quality.
<?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. - - 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. + As a program grows and goes through + multiple reworks and rewrites many initial assumptions about the code + and relation between different parts invalidated. Some implementation + details and approaches became outdated and code quality deteriorates. A + dIfferent picture is if the code uses annotations to express important + properties. Any changes in an underlying code lead to a restarting of a + reasoning procedure which adjust implementation decisions to be as + optimal in a changed environment as before.
diff --git a/documentation/index.xml b/documentation/index.xml index 1388e4b..7e573d3 100644 --- a/documentation/index.xml +++ b/documentation/index.xml @@ -1,249 +1,338 @@ <?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" is all about a developer oriented side focused on 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 three dimensions: efficiency, safety, and flexibility. Unfortunately, those properties are proved to be largely contradictory, for it is manageable to write either efficient (yet unsafe) or safe (yet impractical) code, but not both. Thus, the ultimate goal of the language is to allow developers to produce code that would have all these properties at the same time. Blending features of seemingly incompatible programming paradigms is a basis of Xreate's design principles. To achieve the aforementioned design goals, Xreate consists of three distinctive layers: Brute. The lowest layer is called Brute — this is code that is intended 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 alone is not enough to constitute a full-fledged language since 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 declarative type layer called Transcend. Transcend is a logic reasoner that is appropriate to do management-type work — it analyzes, oversees and controls Brute by guiding compilation process. More precisely, everything on this level, logic or transcend facts and rules, is gathered and sent to an external logic solver to make solutions that are brought back in order to guide compilation. Unlike usual static analysis tools, Transcend directly controls compilation(see Basic Example) and able to make decisions even based on data available only at runtime(see Late Transcend) Interpretation. There is also Interpretation — the intermediate level resembling dynamically typed languages that is used as a contact point and interpreter between Brute and Transcend. On a syntactic level, Xreate is a procedural language with extensive use of annotations — arbitrary unconstrained metadata that a software developer can attach to different language constructs, variables and code blocks. Annotations are completely invisible for the compiler proper and used by Transcend more as a suggestion conveying additional information. "a different language constructs": если подразумевается "конструкции разных языков", тогда лучше "different languages' constructs". Если конструкции языка, в целом, то тогда артикль a не нужен There are several extensions already implemented to give a feeling what does this structure can be used for. Containers chapter describes that it is possible to reason about and automatically choose the most appropriate data structure's implementation depending on how it is used in the code. Look at the example below: x = [1, 2, 3]:: [int]. Container x does not have well defined implementation just yet. Only by looking how it is used throughout the code, the compiler is able to decide how exactly to store container's data. Interaction of different components and joint use of external resources is covered by Exploitation: dataA = readFromFile("/some/file"):: string. //here a file is accessed for the very first time dataB = readFromFile("/some/file"):: string. //this is the last time the file is accessed Exploitation reasoning allows to determine when it is the first, last access to a file, in other words, infers access order. As a result, using the data it is possible to automatically initialize / destruct related resources. Virtualization reasoning enables access control if and when it is needed only. Example: openFile("/some/file"):: string; assign_sizo(zoneA). openFile("/some/file"):: string; assign_sizo(zoneB). If the compiler recognizes file access from the different zones, as in this example, it applies an appropriate virtualization strategy enough to ensure that instructions that belong to different zones do not interfere with each other. Unlike "pure", academic languages, Xreate targets safe and reliable usage of effectful computations such as IO that is covered above as well as mutable structures described in the Communication chapter. Note, that the described extensions are not part of the compiler and developers can write their own custom transcend rules to cover other aspects.
<?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 with the same name div called specializations. Each specialization has a guard that defines a 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 that a code author writes two specializations where the first one is a very fast division implementation, while the second one is a very safe division implementation since it checks division by zero, being "unacceptably slow" due to an extra check instruction, though. This is a basis of polymorphism — client's code test is able to work with any specialization, and compiler must decide which one to invoke with the only hint it has — annotation iAmVerySecure in the function test's signature. "provides two specializations" - возможно, лучший вариант "designates/assigns/allocates two specializations". Или даже просто specifies/indicates. (PS заменил на specifies) "unbearably slow" - я бы заменил на более нейтральное "too slow". Unbearable - это скорее об ощущениях человека. Или, если под "unbearably" имеется в виду "недопустимо медленный", тогда - unacceptably slow. All annotations (except entry) are custom defined by developer itself. This is when Transcend comes into play. By adding a 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 the other specialization isn't even compiled. the, потому что их всего две. By providing custom rules it is possible to implement any polymorphism strategy, be it performed statically or dynamically. The example demonstrates basic workflow: Transcend gathers available information about a program such as annotations and using custom rules makes a decision to guide compilation process, particularly by selecting appropriate specializations as in the above example.
+ +
+ + + <?xxe-sn 2fchdmt7vgg 2?>More Advanced Example: Intentions + + Suppose we write a program to prepare a web + page consisting of several blocks(e.g. a header, a footer) constructed + independently by different parts of our program. In order to organise the + code, we express our intention that all the blocks should be sent to a + client in a very specific order: first a header, then a body and footer, + as below: + + name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15 +eov_expect( + webpage, header, body; %we expect body to be sent after header + webpage, body, footer %.. and footer after body +). + + This is similar to Exploitation: + we are working with the external resource webpage + and want to take into account an exact order of resource exploiting + operations. Then, we write code like this: + + send("Header"):: Content; eov_checkpoint(webpage, header). + + We mark the operations we are interesting in + as checkpoints(here + header is the name of the checkpoint + and webpage is the resource the + checkpoint refers to) and want to know are checkpoints executed in the + expected(as defined above) order. + + If it so happens that these blocks are + constructed in the correct order in our program we may + send them immediately. Otherwise, we must cache already constructed + content till a whole page is generated to ensure correctness. In other + words, clearly, there is an opportunity for optimizations for caching has + memory overhead and delays response latency as well. We write two + implementations immediate_output and + cached_output each for the + corresponding case: + + name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15 +//type to store either sending error code or cached content +Content = type variant { + errcode:: int, + message:: string +}. + +//Send immediately: +guard:: immediate_output +{ + send = function(content:: string):: Content + { + errcode(printf("%s", content)) + } +} + +//Cache content to send it later: +guard:: cached_output +{ + send = function(content:: string):: Content + { + message(content) + } +} + + These implementations should be registered + for the compiler to know which one to use: + + name="tests/exploitation.cpp: Exploitation.Doc_ExampleEov_1", lines=15 +eov_analysis( + success, immediate_output; + fail, cached_output +). + + Predicate eov_analysis + compares actual checkpoints execution order with the expected one and + chooses either of two implementations depending on the result. This way, + it is guarantied that immediate sending is chosen only if it is safe to do + so to avoid unnecessary caching. + + Thus we can safely change and adapt our + program knowing that clients always receive web page's content in the + correct order by automatically employing the most efficient content + delivery strategy depending on particular circumstances. +
diff --git a/documentation/manual.json b/documentation/manual.json index 8c9f315..1ca4a56 100644 --- a/documentation/manual.json +++ b/documentation/manual.json @@ -1,36 +1,36 @@ { "book": "Xreate Manual", "pages": [ {"title": "Basics", "pages": [ {"title": "Introduction", "slug": "/", "filename": "index.xml.remarkup", "subtitle": "General information about Xreate"}, {"title": "Build and Run", "slug": "build/", "filename": "build.xml.remarkup", "subtitle": "Getting started"}, {"title": "Syntax", "slug": "syntax/", "filename": "syntax.xml.remarkup", "subtitle": "Xreate syntax in detail"}, {"title": "Modules", "slug": "syntax/modules/", "filename": "modules.xml.remarkup", "subtitle": "How to reuse and combine existing code in Xreate"} ]}, {"title": "Transcend", "pages": [ {"title": "Overview", "slug": "transcend/", "filename": "transcend.xml.remarkup", "subtitle": "Declarative level of Xreate"}, {"title": "Late Transcend", "slug": "transcend/late-transcend/", "filename": "latetranscend.xml.remarkup", "subtitle": "Reasoning is able to work at runtime"} ]}, {"title": "Concepts", "pages": [ {"title": "Polymorphism", "slug": "concepts/polymorphism/", "filename": "polymorphism.xml.remarkup", "subtitle": "Let logic inference choose how program would behave at runtime"}, {"title": "Context", "slug": "concepts/context/", "filename": "context.xml.remarkup", "subtitle": "How to capture and use information about control flow and hierachy within program"}, - {"title": "Interpretation", "slug": "concepts/interpretation/", "filename": "interpretation.xml.remarkup", "subtitle": "Compile time computations and more"} + {"title": "Interpretation", "slug": "concepts/interpretation/", "filename": "interpretation.xml.remarkup", "subtitle": "Compile time computations"} ]}, {"title": "Extensions", "pages": [ {"title": "Containers", "slug": "concepts/containers/", "filename": "containers.xml.remarkup", "subtitle": "Automatic inference about the most apporpriate container implementation"}, {"title": "Communication", "slug": "communication/", "filename": "communication.xml.remarkup", "subtitle": "Safe usage of mutable as well as global variables"}, {"title": "Exploitation", "slug": "exploitation/", "filename": "exploitation.xml.remarkup", "subtitle": "Shared usage of the external resources - initialization and finalization"}, {"title": "Virtualization", "slug": "virtualization/", "filename": "virtualization.xml.remarkup", "subtitle": "Controls access to the external resources or different program's components"} ]}, {"title": "Transcend APIs", "pages": [ {"title": "AST API", "slug": "transcend/ast-api/", "filename": "ast-api.xml.remarkup", "subtitle": ""}, {"title": "Latex API", "slug": "transcend/latex-api/", "filename": "latex-api.xml.remarkup", "subtitle": ""}, {"title": "Modules API", "slug": "transcend/modules-api/", "filename": "modules-api.xml.remarkup", "subtitle": ""} ]} ] } diff --git a/grammar/xreate.ATG b/grammar/xreate.ATG index c8660e7..abdb3f0 100644 --- a/grammar/xreate.ATG +++ b/grammar/xreate.ATG @@ -1,707 +1,707 @@ //TODO add ListLiteral //TODO ExprTyped: assign default(none) type #include "ast.h" #include "ExternLayer.h" #include #include #define wprintf(format, ...) \ char __buffer[100]; \ wcstombs(__buffer, format, 100); \ fprintf(stderr, __buffer, __VA_ARGS__) using namespace std; COMPILER Xreate details::inconsistent::AST* root = nullptr; // current program unit void ensureInitalizedAST(){ if (root == nullptr) root = new details::inconsistent::AST(); } struct { std::stack scopesOld; CodeScope* scope = nullptr; } context; void pushContextScope(CodeScope* scope){ context.scopesOld.push(context.scope); context.scope = scope; } void popContextScope(){ context.scope = context.scopesOld.top(); context.scopesOld.pop(); } int nextToken() { scanner->ResetPeek(); return scanner->Peek()->kind; } bool checkTokenAfterIdent(int key){ if (la->kind != _ident) return false; return nextToken() == key; } bool checkParametersList() { return la->kind == _ident && nextToken() == _lparen; } bool checkInfix() { return la->kind == _ident && nextToken() == _ident; } bool checkIndex() { return la->kind == _ident && nextToken() == _lbrack; } bool checkFuncDecl() { if (la->kind != _ident) return false; int token2 = nextToken(); int token3 = scanner->Peek()->kind; return token2 == _assign && (token3 == _function || token3 == _pre); } bool checkAssignment() { if (la->kind != _ident) return false; scanner->ResetPeek(); int token2 = scanner->Peek()->kind; if (token2 == _lcurbrack) { scanner->Peek(); int token3 = scanner->Peek()->kind; if (token3 != _rcurbrack) return false; int token4 = scanner->Peek()->kind; return token4 == _assign; } return token2 == _assign; } void recognizeIdentifier(Expression& i){ if (!context.scope->recognizeIdentifier(i)){ root->postponeIdentifier(context.scope, i); } } enum SwitchKind{SWITCH_NORMAL, SWITCH_META}; CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". any = ANY - '"'. digit = "0123456789". cr = '\r'. lf = '\n'. tab = '\t'. TOKENS ident = (letter | '_') {letter | digit | '_'}. number = (digit | '-' digit) {digit}. string = '"' { any } '"'. function = "function". pre = "pre". comma = ','. period = '.'. lparen = '('. rparen = ')'. lbrack = '['. rbrack = ']'. lcurbrack = '{'. rcurbrack = '}'. equal = "==". assign = '='. implic = '-' '>'. colon = ':'. context = "context". tagcolon = "::". lse = "<=". lss = "<". gte = ">=". gtr = ">". ne1 = "!=". ne2= "<>". COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab PRODUCTIONS Xreate = (. Function* function; ensureInitalizedAST(); .) -{( RuleDecl - | InterfaceData | Imprt | GuardSection +{( //RuleDecl + InterfaceData | Imprt | GuardSection | IF(checkFuncDecl()) FDecl (. root->add(function); .) | TDecl | SkipModulesSection )} (. .) . Ident = ident (. name = t->val; .). VarIdent = ident (. e = Expression(Atom(t->val)); .) [ lcurbrack ( ident (. SemErr(coco_string_create("var version as ident is not implemented yet")); .) | number (. Attachments::put(e, Atom(t->val).get()); .) ) rcurbrack ] . FDecl = (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; bool flagIsPrefunct = false; Expression binding; .) Ident assign [pre (. flagIsPrefunct = true; .)] function (. f = new Function(fname); f->isPrefunction = flagIsPrefunct; CodeScope* entry = f->getEntryScope(); .) [lparen Ident tagcolon ExprAnnotations (. f->addBinding(Atom(argName), move(binding)); .) {comma Ident tagcolon ExprAnnotations (. f->addBinding(Atom (argName), move(binding));.) } rparen] [ tagcolon ( IF(flagIsPrefunct) FnTag | Type ) {';' FnTag }] BDecl (. const_cast(entry->getBody()).bindType(move(typOut));.) . GuardSection<>= (. Expression guard; Function* f; .) "guard" tagcolon MetaSimpExpr lcurbrack { FDecl (. f->guard = guard; root->add(f); .) } rcurbrack. /** * TYPES * */ TypeTerm = (. std::wstring tid; .) ("string" (. typ = TypePrimitive::String;.) | "num" (. typ = TypePrimitive::Num;.) | "int" (. typ = TypePrimitive::Int;.) | "float" (. typ = TypePrimitive::Float;.) | "bool" (. typ = TypePrimitive::Bool; .) | "i8" (. typ = TypePrimitive::I8; .) | "i32" (. typ = TypePrimitive::I32; .) | "i64" (. typ = TypePrimitive::I64; .) ). Type = (. TypeAnnotation typ2; TypePrimitive typ3; std::wstring tid; std::string field; .) ( TList | TRecord | TVariant | TSlave | TypeTerm (. typ = typ3; .) | IF (checkIndex()) Ident lbrack TypeIndex (. typ = TypeAnnotation(TypeOperator::ACCESS, {}); typ.__valueCustom = Atom(tid).get(); typ.fields.push_back(field); .) {comma TypeIndex (. typ.fields.push_back(field); .) } rbrack | Ident (. typ = TypeAnnotation(TypeOperator::CUSTOM, {}); typ.__valueCustom = Atom(tid).get(); .) [lparen Type (. typ.__operator = TypeOperator::CALL; typ.__operands.push_back(typ2); .) {comma Type (. typ.__operands.push_back(typ2); .) } rparen] | '*' (.typ = TypeAnnotation(); .) ) . TypeIndex = ( number (. name = Atom(t->val).get(); .) | string (. name = Atom(t->val).get(); .) ) . TList = (. TypeAnnotation ty; .) lbrack Type rbrack (. typ = TypeAnnotation(TypeOperator::LIST_ARRAY, {ty}); .) . TRecord = (. TypeAnnotation t; std::wstring key; size_t keyCounter=0; .) lcurbrack ( IF(checkTokenAfterIdent(_tagcolon)) Ident tagcolon Type | Type (. key = to_wstring(keyCounter++); .) ) (. typ = TypeAnnotation(TypeOperator::LIST_RECORD, {t}); typ.fields.push_back(Atom(key).get()); .) {comma ( IF(checkTokenAfterIdent(_tagcolon)) Ident tagcolon Type | Type (. key = to_wstring(keyCounter++); .) ) (. typ.__operands.push_back(t); typ.fields.push_back(Atom(key).get()); .) } rcurbrack. TVariant= (. TypeAnnotation t, typVoid; std::vector operands; std::vector> keys; std::wstring variant; .) "variant" lcurbrack Ident (. t=typVoid; .) [tagcolon Type] (. keys.push_back(Atom(variant)); operands.push_back(t); .) {comma Ident (. t=typVoid; .) [tagcolon Type] (. keys.push_back(Atom(variant)); operands.push_back(t); .) } rcurbrack (. typ = TypeAnnotation(TypeOperator::VARIANT, {}); typ.__operands = operands; typ.addFields(std::move(keys)); .) . TSlave= (. std::wstring identMaster; .) "slave" Ident (. typ = TypeAnnotation(TypeOperator::SLAVE, {}); typ.__valueCustom = Atom(identMaster).get(); .) . TDecl = (. TypeAnnotation t; std::wstring tname, arg; std::vector> args; .) Ident assign "type" [lparen Ident (. args.push_back(Atom(arg)); .) {comma Ident (. args.push_back(Atom(arg)); .) } rparen] Typeperiod (. t.addBindings(move(args)); root->add(move(t), Atom(tname)); .) . ContextDecl = (. Expression tag; .) context tagcolon MetaSimpExpr (. scope->tags.push_back(tag); .) {';' MetaSimpExpr (. scope->tags.push_back(tag); .) }. VDecl = (. std::wstring vname; Expression var, value;.) VarIdent assign ExprTyped (. Symbol identSymbol = f->addDefinition(move(var), move(value)); Attachments::put(value, identSymbol); .) . BDecl = lcurbrack (. Expression body; pushContextScope(scope); .) {(IF(checkAssignment()) VDecl period - | RuleContextDecl +// | RuleContextDecl | ContextDeclperiod | ExprTyped (. scope->setBody(body); Attachments::put(body, Symbol{ScopedSymbol::RetSymbol, scope});.) )} rcurbrack (. popContextScope(); .) . IfDecl = (. Expression cond; ManagedScpPtr blockTrue = root->add(new CodeScope(context.scope)); ManagedScpPtr blockFalse = root->add(new CodeScope(context.scope)); .) "if" lparen Expr rparen (. e = Expression(Operator::IF, {cond}); .) tagcolon ExprAnnotations BDecl<&*blockTrue> "else" BDecl<&*blockFalse> (. e.addBlock(blockTrue); e.addBlock(blockFalse); .) . LoopDecl = (. Expression eIn, eAcc, eFilters; std::wstring varEl, varAcc, contextClass; Expression tagsEl; ManagedScpPtr block = root->add(new CodeScope(context.scope)); .) "loop" ( "map" lparen Expr implic Ident (. e = Expression(Operator::MAP, {eIn}); .) tagcolon ExprAnnotations rparen tagcolon ExprAnnotations (. e.addBindings({Atom(varEl)}); block->addBinding(Atom(varEl), move(tagsEl)); .) BDecl<&*block> (. e.addBlock(block); .) | "fold" lparen Expr implic Ident tagcolon ExprAnnotations ['|' Expr ] comma Expr implic Identrparen (. e = Expression(Operator::FOLD, {eIn, eAcc}); e.addBindings({Atom(varEl), Atom(varAcc)}); .) tagcolon ExprAnnotations (. block->addBinding(Atom(varEl), move(tagsEl)); block->addBinding(Atom(varAcc), Expression()); .) BDecl<&*block> (. e.addBlock(block); .) | lparen Expr implic Ident rparen (. e = Expression(Operator::INF, {eAcc}); e.addBindings({Atom(varAcc)}); block->addBinding(Atom(varAcc), Expression()); .) tagcolon ExprAnnotations BDecl<&*block> (. e.addBlock(block); .) ). // Switches SwitchDecl = (. TypeAnnotation typ; eSwitch = Expression(Operator::SWITCH, {}); Expression eCondition; Expression tag;.) "switch" ( SwitchVariantDecl | SwitchLateDecl | lparen ExprTyped rparen tagcolon ExprAnnotations (. eSwitch.operands.push_back(eCondition);.) CaseDecl {CaseDecl} ) . CaseDecl = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); Expression condition; .) "case" ( IF(flagSwitchKind == SWITCH_META) lparen MetaSimpExpr rparen BDecl<&*scope> (. Expression exprCase(Operator::CASE, {}); exprCase.addTags({condition}); exprCase.addBlock(scope); outer.addArg(move(exprCase));.) | "default" BDecl<&*scope> (. Expression exprCase(Operator::CASE_DEFAULT, {}); exprCase.addBlock(scope); outer.operands.insert(++outer.operands.begin(), exprCase); .) | lparen CaseParams<&*scope> rparen (. ManagedScpPtr scopeBody = root->add(new CodeScope(&*scope)); Expression exprCase(Operator::CASE, {}); .) BDecl<&*scopeBody> (. exprCase.addBlock(scope); exprCase.addBlock(scopeBody); outer.addArg(move(exprCase)); .) ). CaseParams = (. Expression condition; Expression guard(Operator::LOGIC_AND, {}); pushContextScope(scope); .) ExprTyped (. guard.addArg(Expression(condition)); .) {comma ExprTyped (. guard.addArg(Expression(condition)); .) } (. scope->setBody(guard); popContextScope(); .) . SwitchLateDecl = (. std::wstring aliasCondition; Expression exprCondition, aliasAnns; expr = Expression(Operator::SWITCH_LATE, {}); ManagedScpPtr scope = root->add(new CodeScope(context.scope)); .) "late" lparen Expr [implic Ident] [tagcolon ExprAnnotations] rparen tagcolon ExprAnnotations BDecl<&*scope> (. expr.addArg(Expression(exprCondition)); expr.addBlock(scope); std::string alias; if(aliasCondition.empty()){ if(exprCondition.__state != Expression::IDENT){ SemErr(coco_string_create("An identifier expected in the short form")); return; } //Use exprCondition as identifier alias = exprCondition.getValueString(); } else { //Use aliasCondition alias = Atom(move(aliasCondition)).get(); } expr.addBindings({Atom(string(alias))}); scope->addBinding(Atom(move(alias)), move(aliasAnns)); .) . SwitchVariantDecl = (. Expression varTested; std::wstring varAlias; bool flagAliasFound = false; expr = Expression(Operator::SWITCH_VARIANT, {}); .) "variant" lparen Expr [implic Ident (. flagAliasFound = true; .) ] [tagcolon ExprAnnotations] rparen tagcolon ExprAnnotations (. expr.addArg(std::move(varTested)); if (flagAliasFound) { expr.addBindings({Atom(varAlias)}); } else { if(varTested.__state == Expression::IDENT){ expr.addBindings({Atom(string(varTested.getValueString()))}); } } .) CaseVariantDecl {CaseVariantDecl} . CaseVariantDecl = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); std::wstring key; scope->addBinding(Atom(string(expr.bindings.front())), Expression()); .) "case" lparen Ident rparen (. expr.addArg(root->recognizeVariantConstructor(Atom(std::move(key)))); .) BDecl<&*scope> (. expr.addBlock(scope); .) . IntrinsicDecl= (. std::wstring name; .) "intrinsic" ( Ident< name> (. outer = Expression(Operator::CALL_INTRINSIC, {}); outer.setValue(Atom(name)); .) lparen [CalleeParams] rparen | "query" (. outer = Expression(Operator::QUERY, {}); .) ( "late" IntrinsicQueryLateDecl | lparen [CalleeParams] rparen ) ). IntrinsicQueryLateDecl = (. std::wstring predicateAlias; Expression predicateE, predicateAnns; expr = Expression(Operator::QUERY_LATE, {}); ManagedScpPtr scope = root->add(new CodeScope(context.scope)); .) lparen Expr implic Ident tagcolon ExprAnnotations rparen tagcolon ExprAnnotations BDecl<&*scope> (. expr.addArg(move(predicateE)); expr.addBindings({Atom(wstring(predicateAlias))}); scope->addBinding(Atom(move(predicateAlias)), move(predicateAnns)); expr.addBlock(scope); .) . SequenceDecl = (. sequence = Expression(); sequence.setOp(Operator::SEQUENCE); ManagedScpPtr scope = root->add(new CodeScope(context.scope)); .) "seq" BDecl<&*scope> (. sequence.blocks.push_back(&*scope); scope = root->add(new CodeScope(&*scope)); .) { (. scope = root->add(new CodeScope(&*scope)); .) BDecl<&*scope> (. sequence.blocks.push_back(&*scope); .) }. /*============================ INTERFACES ===============================*/ Imprt<> = "import" "raw" lparen string (. root->__rawImports.push_back(Atom(t->val).get()); .) rparen period. InterfaceData<> = "interface" lparen ( "dfa" rparen InterfaceDFA | "extern-c" rparen InterfaceExternC | "cfa" rparen InterfaceCFA ). InterfaceExternC<> = (. ExternData data; .) lcurbrack {IncludeExternDecl | LibExternDecl } rcurbrack (. root->addExternData(move(data)); .) . LibExternDecl = (. std::wstring pkgname, libname; .) Ident assign "library" tagcolon "pkgconfig" lparen string (. pkgname = t->val; .) rparen period (. data.addLibrary(Atom(libname), Atom(pkgname)); .) . IncludeExternDecl = (. Expression inc; .) "include" ListLiteral period (. data.addIncludeDecl(move(inc)); .) . InterfaceDFA<> = lcurbrack { InstructDecl } rcurbrack . InstructDecl = (.Operator op; Expression tag; Expression scheme; std::vector& tags = scheme.operands; tags.push_back(Expression()); /* return value */ .) "operator" InstructAlias tagcolon lparen (.scheme.setOp(op); .) [ MetaSimpExpr (. tags.push_back(tag); .) { comma MetaSimpExpr (. tags.push_back(tag); .) } ] rparen [ implic MetaSimpExpr (. tags[0] = tag; .) ] (. root->addDFAData(move(scheme)); .) period. InstructAlias = ( "map" (. op = Operator::MAP; .) | "list_range" (. op = Operator::LIST_RANGE; .) | "list" (. op = Operator::LIST; .) | "fold" (. op = Operator::FOLD; .) | "index" (. op = Operator::INDEX; .) ). InterfaceCFA<> = lcurbrack { InstructCFADecl } rcurbrack . InstructCFADecl<> = (.Operator op; Expression tag; Expression scheme; std::vector& tags = scheme.operands; .) "operator" InstructAlias tagcolon (. scheme.setOp(op); .) [ MetaSimpExpr (. tags.push_back(tag); .) { comma MetaSimpExpr (. tags.push_back(tag); .) } ] period (. root->addInterfaceData(CFA, move(scheme)); .). /*============================ METAPROGRAMMING ===============================*/ // TagsDecl = (. Expression tag; TagModifier mod = TagModifier::NONE; .) // ':' { MetaSimpExpr (. /*f.addTag(std::move(tag), mod); */ .) // }. FnTag = (. Expression tag; TagModifier mod = TagModifier::NONE; .) MetaSimpExpr ['-' TagMod] (. f->addTag(std::move(tag), mod); .). TagMod = ( "assert" (. mod = TagModifier::ASSERT; .) | "require" (. mod = TagModifier::REQUIRE; .) ). -RuleDecl<> = -"rule" tagcolon (. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .) -lparen Ident tagcolon Domain (. args.add(arg, typ); .) -{comma Ident tagcolon Domain (. args.add(arg, typ); .) -} rparen -["case" RGuard {comma RGuard}] -lcurbrack RBody rcurbrack . +// RuleDecl<> = +// "rule" tagcolon (. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .) +// lparen Ident tagcolon Domain (. args.add(arg, typ); .) +// {comma Ident tagcolon Domain (. args.add(arg, typ); .) +// } rparen +// ["case" RGuard {comma RGuard}] +// lcurbrack RBody rcurbrack . /* - TODO use RGuard for guards-*/ -RuleContextDecl = (.Expression eHead, eGuards, eBody; .) -"rule" "context" tagcolon MetaSimpExpr -"case" lparen MetaSimpExpr rparen -lcurbrack MetaSimpExpr rcurbrack (.scope->contextRules.push_back(Expression(Operator::CONTEXT_RULE, {eHead, eGuards, eBody})); .). - -Domain = -( - "function" (. dom = DomainAnnotation::FUNCTION; .) - | "variable" (. dom = DomainAnnotation::VARIABLE; .) -). - -RGuard= (. Expression e; .) -MetaExpr (. guards.add(std::move(e)); .). - -MetaExpr= (.Operator op; Expression e2; .) -MetaExpr2 -[MetaOp MetaExpr2 (. e = Expression(op, {e, e2}); .) -]. - -MetaExpr2= -( -lparen MetaExpr rparen -| MetaSimpExpr -). +// RuleContextDecl = (.Expression eHead, eGuards, eBody; .) +// "rule" "context" tagcolon MetaSimpExpr +// "case" lparen MetaSimpExpr rparen +// lcurbrack MetaSimpExpr rcurbrack (.scope->contextRules.push_back(Expression(Operator::CONTEXT_RULE, {eHead, eGuards, eBody})); .). + +// Domain = +// ( +// "function" (. dom = DomainAnnotation::FUNCTION; .) +// | "variable" (. dom = DomainAnnotation::VARIABLE; .) +// ). + +// RGuard= (. Expression e; .) +// MetaExpr (. guards.add(std::move(e)); .). + +// MetaExpr= (.Operator op; Expression e2; .) +// MetaExpr2 +// [MetaOp MetaExpr2 (. e = Expression(op, {e, e2}); .) +// ]. + +// MetaExpr2= +// ( +// lparen MetaExpr rparen +// | MetaSimpExpr +// ). MetaSimpExpr= (. std::wstring i1, infix; Expression e2; .) ( '-' MetaSimpExpr (. e = Expression(Operator::NEG, {e2}); .) | IF(checkParametersList()) Ident (. e = Expression(Operator::CALL, {Expression(Atom(i1))}); .) lparen [ MetaCalleeParams ] rparen | IF(checkInfix()) Ident Ident MetaSimpExpr (. e = Expression(Operator::CALL, {Expression(Atom(infix))}); e.addArg(Expression(Atom(i1))); e.addArg(std::move(e2)); .) | Ident (. e = Expression(Operator::CALL, {Atom(i1)}); .) ). MetaCalleeParams = (. Expression e2; .) MetaSimpExpr (. e.addArg(Expression(e2)); .) {comma MetaSimpExpr (. e.addArg(Expression(e2)); .) }. -RBody = - (. Expression e; std::wstring msg; .) - "warning" MetaExpr ["message" string (. msg = t->val; .) -] (. root->add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom(msg))); .) - . +// RBody = +// (. Expression e; std::wstring msg; .) +// "warning" MetaExpr ["message" string (. msg = t->val; .) +// ] (. root->add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom(msg))); .) +// . -MetaOp< Operator& op> = - implic (. op = Operator::IMPL; .) -. +// MetaOp< Operator& op> = +// implic (. op = Operator::IMPL; .) +// . /*============================ Expressions ===============================*/ ExprAnnotations = (. TypeAnnotation typ; std::list tags; Expression tag; e.tags.clear();.) Type (. e.bindType(move(typ)); .) {';' MetaSimpExpr (. tags.push_back(tag); .) } (. e.addTags(tags); .) . ExprTyped = Expr [tagcolon ExprAnnotations]. Expr< Expression& e> (. Operator op; Expression e2; .) = ExprArithmAdd [ RelOp ExprArithmAdd (. e = Expression(op, {e, e2}); .) ]. ExprArithmAdd< Expression& e>= (. Operator op; Expression e2; .) ExprArithmMul< e> [ AddOp< op> ExprArithmAdd< e2> (. e = Expression(op, {e, e2});.) ]. ExprArithmMul< Expression& e> (. Operator op; Expression e2; .) = ExprPostfix< e> [ MulOp< op> ExprArithmMul< e2> (. e = Expression(op, {e, e2}); .) ]. ExprPostfix = Term [ (. e = Expression(Operator::INDEX, {e}); .) {lbrack CalleeParams rbrack } ]. Term< Expression& e> (. std::wstring name; e = Expression(); .) = (IF (checkParametersList()) Ident< name> (. e = Expression(Operator::CALL, {Atom(name)}); root->recognizeVariantConstructor(e); .) lparen [CalleeParams] rparen | VarIdent (. recognizeIdentifier(e); .) | ListLiteral | ListRangeLiteral | LoopDecl | IfDecl | SwitchDecl | IntrinsicDecl | SequenceDecl | number (. e = Expression(Atom(t->val)); .) | string (. e = Expression(Atom(t->val)); .) | "true" (. e = Expression(Atom(1)); e.bindType(TypePrimitive::Bool); .) | "false" (. e = Expression(Atom(0)); e.bindType(TypePrimitive::Bool); .) | "undef" (. e = Expression(Operator::UNDEF, {}); .) | '-' Term (. e = Expression(Operator::NEG, {e}); .) | lparen ExprTyped rparen ). ListLiteral = (. std::wstring key; Expression val; std::list> keys; size_t keyCounter=0; .) lcurbrack (IF(checkTokenAfterIdent(_assign)) Ident assign Expr | Expr (. key = to_wstring(keyCounter++); .) ) (. keys.push_back(Atom(key)); e = Expression(Operator::LIST, {val}); .) {comma (IF(checkTokenAfterIdent(_assign)) Ident assign Expr | Expr (. key = to_wstring(keyCounter++); .) ) (. e.addArg(Expression(val)); keys.push_back(Atom(key)); .) } rcurbrack (. e.addBindings(keys.begin(), keys.end()); .) . ListRangeLiteral = (. Expression eFrom, eTo; .) lbrack Expr ".." Expr rbrack (. e = Expression(Operator::LIST_RANGE, {eFrom, eTo}); .) . CalleeParams = (. Expression e2; .) ExprTyped (. e.addArg(Expression(e2)); .) {comma ExprTyped (. e.addArg(Expression(e2)); .) }. AddOp< Operator& op> = (. op = Operator::ADD; .) ( '+' | '-' (. op = Operator::SUB; .) ). MulOp< Operator& op> = (. op = Operator::MUL; .) ( '*' | '/' (. op = Operator::DIV; .) ). RelOp< Operator& op> = (. op = Operator::EQU; .) ( equal | (ne1 | ne2) (. op = Operator::NE; .) | lse (. op = Operator::LSE; .) | lss (. op = Operator::LSS; .) | gte (. op = Operator::GTE; .) | gtr (. op = Operator::GTR; .) ). SkipModulesSection = "module" {ANY} (lcurbrack {ANY} rcurbrack | '.'). END Xreate. diff --git a/scripts/execution-order/execorder.lp b/scripts/execution-order/execorder.lp new file mode 100644 index 0000000..7c3fbad --- /dev/null +++ b/scripts/execution-order/execorder.lp @@ -0,0 +1,33 @@ +% INPUT: +% - POS expected execution order +% eov_expect(Resource, ScopeBefore, ScopeAfter). +% +% OUTPUT: +% - does actual execution order match expected one + +%cfa_payload(X, Node, Path). + +% --------------------------------- +% Find root of the Expectation POS: + +cfa_payload(eov_payload(Resource, Pprev), scope(Sprev), ()):- + eov_expect(Resource, Pprev, _); + bind(Symprev, eov_checkpoint(Resource, Pprev)); + Symprev = s(_, _, Sprev). + +eov_results(Resource, fail):- + eov_expect(Resource, Pprev, Pnext); + not cfa_payload(eov_payload(Resource, Pprev), scope(Snext), ()); + bind(Symnext, eov_checkpoint(Resource, Pnext)); + Symnext = s(_, _, Snext). + +eov_results(Resource, success):- + not eov_results(Resource, fail); + eov_expect(Resource, _, _). + +dfa_callguard(Sym, Impl):- + bind(Sym, eov_checkpoint(Resource, _)); + eov_results(Resource, Result); + eov_analysis(Result, Impl). + + diff --git a/scripts/execution-order/test-1.assembly.lp b/scripts/execution-order/test-1.assembly.lp new file mode 100644 index 0000000..3515920 --- /dev/null +++ b/scripts/execution-order/test-1.assembly.lp @@ -0,0 +1,5 @@ +#include "scripts/cfa/payload.lp". +#include "scripts/execution-order/execorder.lp". + + + diff --git a/scripts/execution-order/test-1.xreate b/scripts/execution-order/test-1.xreate new file mode 100644 index 0000000..239d1a8 --- /dev/null +++ b/scripts/execution-order/test-1.xreate @@ -0,0 +1,49 @@ +// UnitTest: tests/exploitation.cpp, Exploitation.Doc_ExampleEov_1 + +interface(extern-c){ + externalLibs = library:: pkgconfig("libxml-2.0"). + + include { + externalLibs = {"scripts/virtualization/defs.h"} + }. +} + + + +print = function(content:: Content):: int +{ + switch variant(content):: int + case (errcode) {content} + case (message) {printf("%s", content)} +} + +sendResponse = function(header:: Content, body:: Content, footer:: Content):: int +{ + seq + {print(header)} + {print(body)} + {print(footer)} +} + +main = function:: int; entry +{ + seq + { + header = send("Header"):: Content; eov_checkpoint(webpage, header). + header + } + + { + body = send("Body"):: Content; eov_checkpoint(webpage, body). + body + } + + { + footer = send("Footer"):: Content; eov_checkpoint(webpage, footer). + footer + } + + { + sendResponse(header, body, footer) + } +}