diff --git a/config/default.json b/config/default.json index c5f1fcf..f551dc0 100644 --- a/config/default.json +++ b/config/default.json @@ -1,75 +1,36 @@ { - "containers": { - "id": { - "implementations": "containers_impl", - "linkedlist": "linkedlist" - }, - - "impl": { - "solid": "solid", - "onthefly": "onthefly" - } - }, + "template": "problems", + + "templates": { + "bugs":"Containers.DONE_ArrayUpdate1", - "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":"*", - "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": "*", - "universal": "Universal.*", - "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.*" - } - } + "default": "*-Colevels.*:Interpretation.*", + "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.*", + "ast": "AST.*", + "effects": "Effects.*", + "basic": "Attachments.*", + "dimensions": "Dimensions.CompileDeepAnn1", + "compilation": "Compilation.*", + "communication": "Communication.*", + "cfa": "CFA.*", + "containers": "Containers.DONE_*", + "dfa": "DFA.*", + "diagnostic": "Diagnostic.*", + "dsl": "Interpretation.*:Association.*", + "exploitation": "Exploitation.*", + "ExpressionSerializer": "ExpressionSerializer.*", + "externc": "InterfaceExternC.*", + "loops": "Loop.*", + "latereasoning": "LateReasoning.*", + "latex": "Latex.*", + "modules": "Modules.*", + "polymorphs": "PDT.BuildDTTable1", + "problems": "Problems.MinMax1", + "intrinsic-query": "Types.SlaveTypes*:Association.TypedQuery*", + "types": "Types.*", + "universal": "Universal.*", + "virtualization": "Virtualization.*", + "vendorsAPI/clang": "ClangAPI.*", + "vendorsAPI/xml2": "libxml2.*" + } } diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index d1084db..dd46244 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -1,34 +1,32 @@ project(Xreate) cmake_minimum_required(VERSION 2.8.11) -set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -fprofile-arcs -ftest-coverage -O0") -set(CMAKE_BUILD_TYPE Debug) +include (Configurations.cmake) + # BUILD OPTIONS #====================== -set(XREATE_DEFINITIONS - -D_GNU_SOURCE -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DWITH_THREADS=1 -) +set(CMAKE_BUILD_TYPE Debug) add_definitions(${XREATE_DEFINITIONS}) add_compile_options(-Winvalid-pch -fPIC -std=c++14) # XREATE #====================== add_subdirectory(src) # XREATE-TESTS #====================== if (BUILD_XREATE_TESTS) message ("Building xreate tests") add_subdirectory(tests) endif () # XREATE-SERVER #====================== if (BUILD_XREATE_SERVER) message ("Building xreate server") add_subdirectory(../tools/execution-server execution-server) endif () diff --git a/cpp/Configurations.cmake b/cpp/Configurations.cmake new file mode 100644 index 0000000..df7021e --- /dev/null +++ b/cpp/Configurations.cmake @@ -0,0 +1,157 @@ +cmake_minimum_required(VERSION 2.8.11) + +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -fprofile-arcs -ftest-coverage -O0") + + +set(XREATE_DEFINITIONS_COMMON + -D_GNU_SOURCE + -D__STDC_CONSTANT_MACROS + -D__STDC_FORMAT_MACROS + -D__STDC_LIMIT_MACROS + -DWITH_THREADS=1 +) +#------------------------------------------------------ +# CONFIGURATION: Min + +set(XREATE_SOURCE_FILES_MIN + analysis/typeinference.cpp + xreatemanager.cpp + transcendlayer.cpp + llvmlayer.cpp + pass/compilepass.cpp + analysis/utils.cpp + ast.cpp + aux/xreatemanager-decorators.cpp + compilation/transformations.cpp + compilation/transformersaturation.cpp + attachments.cpp + compilation/control.cpp + utils.cpp + pass/abstractpass.cpp + aux/serialization/expressionserializer.cpp + analysis/transcendtarget.cpp analysis/resources.cpp + query/containers.cpp + modules.cpp + compilation/containers.cpp + compilation/containers/arrays.cpp +) + +set(XREATE_TEST_FILES_MIN + universal.cpp + introduction.cpp + unit-test-example.cpp + supplemental/docutils + transcend.cpp + association.cpp + main.cpp + attachments.cpp + ast.cpp + compilation.cpp + ExpressionSerializer.cpp + types.cpp + #vendorsAPI/clangAPI.cpp + #vendorsAPI/xml2.cpp + #vendorsAPI/json.cpp + loops.cpp + #supplemental/versions-algorithm-data_dependency.cpp + supplemental/basics.cpp +) + +IF(XREATE_CONFIG STREQUAL "Min") + set(XREATE_SOURCE_FILES + ${XREATE_SOURCE_FILES_MIN} + ) + + set(XREATE_TEST_FILES ${XREATE_TEST_FILES_MIN}) + + set(XREATE_DEFINITIONS ${XREATE_DEFINITIONS_COMMON} + -DXREATE_CONFIG_MIN + ) +ENDIF() + +#------------------------------------------------------ +# CONFIGURATION: Default + +set(XREATE_SOURCE_FILES_DEFAULT ${XREATE_SOURCE_FILES_MIN} + compilation/targetinterpretation.cpp + analysis/temporalseqgraph.cpp + pass/cfatemporalseqpass.cpp + analysis/cfagraph.cpp + pass/cfapass.cpp + compilation/interpretation-instructions.cpp + ExternLayer.cpp + analysis/cfagraph.cpp + compilation/latetranscend.cpp + query/latex.cpp + aux/latereasoning.cpp + analysis/dfagraph.cpp + pass/dfapass.cpp + pass/interpretationpass.cpp + pass/versionspass.cpp + contextrule.cpp + compilation/demand.cpp + analysis/predefinedanns.cpp +) + +set(XREATE_TEST_FILES_DEFAULT ${XREATE_TEST_FILES_MIN} + interpretation.cpp + transcend-ast.cpp + cfa.cpp + latetranscend.cpp + latex.cpp + polymorph.cpp + virtualization.cpp + exploitation.cpp + effects-communication.cpp + modules.cpp + dfa.cpp + effects-versions.cpp + containers.cpp + externc.cpp + aux/expressions.cpp + polymorphism-dt.cpp +) + +IF(XREATE_CONFIG STREQUAL "Default") + set(XREATE_SOURCE_FILES + ${XREATE_SOURCE_FILES_DEFAULT} + ) + + set(XREATE_TEST_FILES ${XREATE_TEST_FILES_DEFAULT}) + + set(XREATE_DEFINITIONS ${XREATE_DEFINITIONS_COMMON} + XREATE_ENABLE_EXTERN + ) +ENDIF() + +#------------------------------------------------------ +# CONFIGURATION: Dimensions + +IF(XREATE_CONFIG STREQUAL "Dimensions") + set(XREATE_SOURCE_FILES ${XREATE_SOURCE_FILES_MIN} + query/demand.cpp + compilation/demand.cpp + query/polymorph.cpp + compilation/polymorph.cpp + aux/expressions.cpp + pass/interpretationpass.cpp + compilation/targetinterpretation.cpp + compilation/intrinsics.cpp +# compilation/containerinst.cpp + analysis/predefinedanns.cpp + analysis/typehints.cpp + ) + + set(XREATE_TEST_FILES ${XREATE_TEST_FILES_MIN} + interpretation.cpp + dimensions.cpp + polymorph.cpp + polymorphism-dt.cpp + containers.cpp + problems.cpp + ) + + set(XREATE_DEFINITIONS ${XREATE_DEFINITIONS_COMMON} + -DXREATE_CONFIG_MIN + ) +ENDIF() diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index ff23a2c..2b91e83 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -1,247 +1,197 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate) cmake_policy(SET CMP0022 NEW) message("MODULES" ${CMAKE_MODULE_PATH}) # LLVM #====================== FIND_PACKAGE (LLVM REQUIRED) set(LLVM_VERSION ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Native arch: ${LLVM_NATIVE_ARCH}") message("LLVM LIB PATH:" ${LLVM_LIBRARY_DIRS}) message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") INCLUDE_DIRECTORIES(${LLVM_INCLUDE_DIRS}) message(STATUS "INCLUDE DIR: ${LLVM_INCLUDE_DIRS}") add_definitions(${LLVM_DEFINITIONS}) message(STATUS "LLVM DEFS: " ${LLVM_DEFINITIONS}) execute_process( COMMAND llvm-config --libs OUTPUT_VARIABLE LLVM_LIBS OUTPUT_STRIP_TRAILING_WHITESPACE) message(STATUS "LLVM LIBS: " ${LLVM_LIBS}) # CLANG #====================== set(CLANG_LIBS clangCodeGen clangASTMatchers clangQuery clangTooling clangFrontend clangSerialization clangDriver clangParse clangSema clangAnalysis clangAST clangEdit clangLex clangBasic ) # POTASSCO #====================== set(CLINGO_PATH "/opt/potassco/clingo" CACHE PATH "Path to potassco sources") set(POTASSCO_INCLUDE_PATH ${CLINGO_PATH}/libgringo ${CLINGO_PATH}/libclasp ${CLINGO_PATH}/libclingo ${CLINGO_PATH}/libprogram_opts ${CLINGO_PATH}/liblp ) INCLUDE_DIRECTORIES(${POTASSCO_INCLUDE_PATH}) set(LIBCLASP_LIBS clingo clasp gringo program_opts reify lp ) message("CLASP LIBS: " ${LIBCLASP_LIBS}) - -# OTHER DEPENDENCIES -#=========================== -set(JEAYESON_INCLUDE_PATH - ${CMAKE_HOME_DIRECTORY}/../vendors/jeayeson/include/ -) -INCLUDE_DIRECTORIES(${JEAYESON_INCLUDE_PATH}) - - - # COCO #=========================== set(COCO_EXECUTABLE "" CACHE PATH "Path to coco executable") set(COCO_FRAMES_PATH "" CACHE PATH "Path to coco frames") set(COCO_GRAMMAR_PATH ${CMAKE_HOME_DIRECTORY}/../grammar/) set(COCO_SOURCE_FILES_MAIN ${COCO_GRAMMAR_PATH}/main/Parser.cpp ${COCO_GRAMMAR_PATH}/main/Scanner.cpp ) set(COCO_SOURCE_FILES_MODULES ${COCO_GRAMMAR_PATH}/modules/Parser.cpp ${COCO_GRAMMAR_PATH}/modules/Scanner.cpp ) set(COCO_SOURCE_FILES_UNIVERSAL ${COCO_GRAMMAR_PATH}/universal/Parser.cpp ${COCO_GRAMMAR_PATH}/universal/Scanner.cpp ) set(COCO_SOURCE_FILES ${COCO_SOURCE_FILES_MODULES} ${COCO_SOURCE_FILES_MAIN} ${COCO_SOURCE_FILES_UNIVERSAL} ) INCLUDE_DIRECTORIES(${COCO_GRAMMAR_PATH}) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MAIN} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar main ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/xreate.ATG ) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MODULES} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar modules ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/modules.ATG ) message(STATUS "COCO GRAMMAR BUILD STATUS:" ${COCO_OUTPUT}) # XREATE #====================== -set(SOURCE_FILES - analysis/temporalseqgraph.cpp - pass/cfatemporalseqpass.cpp - analysis/cfagraph.cpp - pass/cfapass.cpp - modules.cpp - compilation/interpretation-instructions.cpp - ExternLayer.cpp - analysis/cfagraph.cpp - compilation/latetranscend.cpp - analysis/interpretation.cpp - query/latex.cpp - query/polymorph.cpp - compilation/polymorph.cpp - aux/latereasoning.cpp - compilation/latex.cpp - analysis/typeinference.cpp - xreatemanager.cpp - transcendlayer.cpp - analysis/dfagraph.cpp - llvmlayer.cpp - pass/compilepass.cpp - analysis/utils.cpp - pass/dfapass.cpp - compilation/targetinterpretation.cpp - pass/interpretationpass.cpp - ast.cpp - aux/xreatemanager-decorators.cpp - compilation/operators.cpp - compilation/transformations.cpp - compilation/transformersaturation.cpp - pass/versionspass.cpp - attachments.cpp - compilation/containers.cpp - compilation/advancedinstructions.cpp - utils.cpp - pass/abstractpass.cpp - contextrule.cpp - query/containers.cpp - aux/serialization/expressionserializer.cpp -) + +#source files defined in configuration.cmake set(XREATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ ) INCLUDE_DIRECTORIES(${XREATE_INCLUDE_DIRS}) set(XREATE_PRIVATE_INCLUDE_DIRS ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} - ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) -add_library(${PROJECT_NAME} SHARED ${COCO_SOURCE_FILES} ${SOURCE_FILES}) +add_library(${PROJECT_NAME} SHARED ${COCO_SOURCE_FILES} ${XREATE_SOURCE_FILES}) target_link_libraries(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} - ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) get_directory_property(DEFINITIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) message("definitions all: " ${DEFINITIONS_ALL}) target_compile_definitions(${PROJECT_NAME} INTERFACE ${DEFINITIONS_ALL}) get_directory_property(COMPILATION_OPTIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) message("compilations all: " ${COMPILATION_OPTIONS_ALL}) target_compile_options(${PROJECT_NAME} INTERFACE ${COMPILATION_OPTIONS_ALL}) SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBCLASP_LIBS} ${CLANG_LIBS} ${LLVM_LIBS} tbb boost_system boost_filesystem ) #${CLANG_LIBS} #set (LINK_INTERFACE_LIBRARIES "") # FUNCTION(PREPEND var prefix) # SET(listVar "") # FOREACH(f ${ARGN}) # LIST(APPEND listVar "${prefix}/${f}") # ENDFOREACH(f) # SET(${var} "${listVar}" PARENT_SCOPE) # ENDFUNCTION(PREPEND) #set(COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "-j4") #cotire(xreate) # MACRO (ADD_PCH_RULE _header_filename _src_list) # SET(_gch_filename "${_header_filename}.gch") # LIST(APPEND ${_src_list} ${_gch_filename}) # SET (_args ${CMAKE_CXX_FLAGS}) # LIST(APPEND _args -c ${_header_filename} -o ${_gch_filename}) # GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES) # foreach (_inc ${DIRINC}) # LIST(APPEND _args "-I" ${_inc}) # endforeach(_inc ${DIRINC}) # SEPARATE_ARGUMENTS(_args) # add_custom_command(OUTPUT ${_gch_filename} # COMMAND rm -f ${_gch_filename} # COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args} # DEPENDS ${_header_filename}) # ENDMACRO(ADD_PCH_RULE _header_filename _src_list) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/ast.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/llvmlayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/clasplayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/pass/abstractpass.h SOURCE_FILES) diff --git a/cpp/src/ExternLayer.cpp b/cpp/src/ExternLayer.cpp index 03afa74..247f833 100644 --- a/cpp/src/ExternLayer.cpp +++ b/cpp/src/ExternLayer.cpp @@ -1,334 +1,321 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * ExternLayer.cpp * * Created on: 4/21/15 * Author: pgess */ #include "ExternLayer.h" #include "clang/Tooling/Tooling.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/ASTMatchers/ASTMatchers.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "clang/Basic/TargetInfo.h" #include "clang/CodeGen/ModuleBuilder.h" #include "clang/CodeGen/CodeGenABITypes.h" #include #include #include #include #include #include using namespace std; using namespace clang; using namespace clang::driver; using namespace clang::tooling; using namespace clang::ast_matchers; using namespace llvm; namespace xreate{ class FinderCallbackTypeDecl : public MatchFinder::MatchCallback{ public: QualType typeResult; virtual void run(const MatchFinder::MatchResult &Result) { if (const TypedefDecl * decl = Result.Nodes.getNodeAs("typename")) { typeResult = decl->getUnderlyingType(); } } } ; class FinderCallbackFunction : public MatchFinder::MatchCallback{ public: QualType typeResult; virtual void run(const MatchFinder::MatchResult &Result) { if (const FunctionDecl * decl = Result.Nodes.getNodeAs("function")) { typeResult = decl->getType(); } } } ; void -ExternData::addLibrary(Atom&& name, Atom&& package) { - __dictLibraries.emplace(name.get(), package.get()); +ExternData::addLibAlias(Atom&& alias, std::string&& package) { + __aliases.emplace(alias.get(), package.get()); } void -ExternData::addIncludeDecl(Expression&& e) { - assert(e.op == Operator::LIST); - - //TODO ?? implement Expression parsing(Array of Expr as vector); - for(size_t i = 0, size = e.operands.size(); i < size; ++i) { - std::string library = e.bindings.at(i); - assert(__dictLibraries.count(library)); - std::string package = __dictLibraries.at(library); - - Expression listHeaders = e.operands.at(i); - assert(listHeaders.op == Operator::LIST); - - std::vector headers; - std::transform(listHeaders.operands.begin(), listHeaders.operands.end(), std::inserter(headers, headers.end()), - [](const Expression & o) { - assert(o.__state == Expression::STRING); - return o.getValueString(); - }); - - entries.emplace_back(ExternEntry{package, std::move(headers)}); - } +ExternData::requireHeaders(const std::list& headers){ + __requiredHeaders.insert( + std::inserter(__requiredHeaders, __requiredHeaders.end()), + headers.begin(), headers.end()); +} + +void +ExternData::requireLibPackage(Atom&& package){ + __requiredPackages.push_back(package.get()); +} + +void +ExternData::requireLibAlias(Atom&& alias){ + assert(__aliases.count(alias.get()); + __requiredPackages.push_back(__aliases.at(alias.get())); } void ExternLayer::addExternalData(const std::vector& data) { entries.insert(entries.end(), data.begin(), data.end()); } ExternLayer::ExternLayer(LLVMLayer *llvm) : __llvm(llvm) { } std::vector -ExternLayer::fetchPackageFlags(const ExternEntry& entry) { +ExternLayer::fetchPackageFlags(const std::string& package) { std::vector args; - FILE* flags = popen((string("pkg-config --cflags ") + entry.package).c_str(), "r"); + FILE* flags = popen((string("pkg-config --cflags ") + package).c_str(), "r"); size_t linesize = 0; char* linebuf = 0; ssize_t linelen = 0; while ((linelen = getdelim(&linebuf, &linesize, ' ', flags)) > 0) { if (linebuf[0] == '\n') continue; if (linelen == 1 && linebuf[0] == ' ') continue; //cut unwanted symbols at the end char symbLast = linebuf[linelen - 1 ]; if (symbLast == ' ' || symbLast == '\n') linebuf[linelen - 1] = 0; //print header for debug purposes llvm::outs() << '<' << linebuf << "> "; args.push_back(linebuf); free(linebuf); linebuf = 0; } pclose(flags); return (args); } std::vector -ExternLayer::fetchPackageLibs(const ExternEntry& entry) { +ExternLayer::fetchPackageLibs(const std::string& package) { std::vector libs; - FILE* flags = popen((string("pkg-config --libs ") + entry.package).c_str(), "r"); + FILE* flags = popen((string("pkg-config --libs ") + package).c_str(), "r"); size_t linesize = 0; char* linebuf = 0; ssize_t linelen = 0; while ((linelen = getdelim(&linebuf, &linesize, ' ', flags)) > 0) { if (linebuf[0] == '\n') continue; if (linelen == 1 && linebuf[0] == ' ') continue; //cut unwanted symbols at the end char symbLast = linebuf[linelen - 1 ]; if (symbLast == ' ' || symbLast == '\n') linebuf[linelen - 1] = 0; //cut unwanted symbols at the beginning if (linelen > 1 && linebuf[0] == '-' && linebuf[1] == 'l') { libs.push_back(linebuf + 2); } else { libs.push_back(linebuf); } //print lib name for debug purposes llvm::outs() << '<' << linebuf << "> "; free(linebuf); linebuf = 0; } pclose(flags); return (libs); } void ExternLayer::loadLibraries(vector&& libs) { string msgErr; for (const string& lib : libs) { const string& libName = string("lib") + lib + ".so"; if (!llvm::sys::DynamicLibrary::LoadLibraryPermanently(libName.c_str(), &msgErr)) { llvm::errs() << "\n" << "Loading library " << lib << ". " << msgErr << "\n"; } } } void -ExternLayer::init(const AST* root) { - addExternalData(root->__externdata); - - // TODO -EXTERN01.DIP, use default include path from 'clang -xc++ -E' - list code; - std::vector args{ - "-I/usr/include" - , "-I/usr/local/include" - , "-I/usr/lib64/clang/5.0.1/include" - // ,"-I/usr/lib/gcc/x86_64-linux-gnu/4.9/include" - // ,"-I/usr/include/x86_64-linux-gnu" - }; - - std::vector libs; - - boost::format formatInclude("#include \"%1%\""); - for(const ExternEntry& entry : entries) { - llvm::outs() << "[ExternC] Processing package: " << entry.package << "\n"; - llvm::outs() << "[ExternC] args: "; - - vector&& args2 = fetchPackageFlags(entry); - args.insert(args.end(), args2.begin(), args2.end()); - for(const string arg : args2) { - llvm::outs() << "<" << arg << "> "; - } - - llvm::outs() << "\n[ExternC] libs: "; - args2 = fetchPackageLibs(entry); - for(const string arg : args2) { - llvm::outs() << "<" << arg << "> "; - } - libs.insert(libs.end(), args2.begin(), args2.end()); - - - llvm::outs() << "\n[ExternC] headers: "; - std::transform(entry.headers.begin(), entry.headers.end(), std::inserter(code, code.begin()), - [&formatInclude](const string header ) { - string line = boost::str(formatInclude % header); - llvm::outs() << "<" << line << "> "; - - return line; - }); - - llvm::outs() << '\n'; - } - - loadLibraries(move(libs)); - ast = buildASTFromCodeWithArgs(boost::algorithm::join(code, "\n"), args); - __llvm->module->setDataLayout(ast->getASTContext().getTargetInfo().getDataLayout()); - __clang.reset(new CompilerInstance()); - __clang->createDiagnostics(); - - __codegen.reset(CreateLLVMCodeGen( - __clang->getDiagnostics(), - __llvm->module->getName(), - __clang->getHeaderSearchOpts(), - __clang->getPreprocessorOpts(), - clang::CodeGenOptions(), - __llvm->llvmContext - )); - - __codegen->Initialize(ast->getASTContext()); +ExternLayer::init(const AST* ast) { + std::vector args{ + "-isystem" + }; + + list code; + std::vector libs; + + boost::format formatInclude("#include \"%1%\""); + for(const ExternData& entry : ast->__externdata) { + llvm::outs() << "[ExternC] Processing package: " << entry.package << "\n"; + llvm::outs() << "[ExternC] args: "; + + vector&& args2 = fetchPackageFlags(entry); + args.insert(args.end(), args2.begin(), args2.end()); + for(const string arg : args2) { + llvm::outs() << "<" << arg << "> "; + } + + llvm::outs() << "\n[ExternC] libs: "; + args2 = fetchPackageLibs(entry); + for(const string arg : args2) { + llvm::outs() << "<" << arg << "> "; + } + libs.insert(libs.end(), args2.begin(), args2.end()); + + + llvm::outs() << "\n[ExternC] headers: "; + std::transform(entry.headers.begin(), entry.headers.end(), std::inserter(code, code.begin()), + [&formatInclude](const string header ) { + string line = boost::str(formatInclude % header); + llvm::outs() << "<" << line << "> "; + + return line; + }); + + llvm::outs() << '\n'; + } + + loadLibraries(move(libs)); + ast = buildASTFromCodeWithArgs(boost::algorithm::join(code, "\n"), args); + __llvm->module->setDataLayout(ast->getASTContext().getTargetInfo().getDataLayout()); + __clang.reset(new CompilerInstance()); + __clang->createDiagnostics(); + + __codegen.reset(CreateLLVMCodeGen( + __clang->getDiagnostics(), + __llvm->module->getName(), + __clang->getHeaderSearchOpts(), + __clang->getPreprocessorOpts(), + clang::CodeGenOptions(), + __llvm->llvmContext + )); + + __codegen->Initialize(ast->getASTContext()); }; bool ExternLayer::isPointer(const clang::QualType &t) { const clang::Type * tInfo = t.getTypePtr(); assert(tInfo); return tInfo->isAnyPointerType(); } llvm::Type* ExternLayer::toLLVMType(const clang::QualType& t) { return CodeGen::convertTypeForMemory( __codegen->CGM(), t); } std::vector ExternLayer::getStructFields(const clang::QualType& ty) { clang::QualType t = ty; if (isPointer(ty)) { const clang::PointerType* tPtr = ty->getAs(); t = tPtr->getPointeeType(); } assert(t.getTypePtr()->isRecordType()); const RecordType *record = t->getAsStructureType(); assert(record); std::vector result; //FieldDecl* field: record->getDecl()->fields() for (auto i = record->getDecl()->field_begin(); i != record->getDecl()->field_end(); ++i) { result.push_back(i->getName()); } return result; } bool ExternLayer::isArrayType(const std::string& type){ clang::QualType typeRaw = lookupType(type); if (isPointer(typeRaw)) { const clang::PointerType* typePtr = typeRaw->getAs(); typeRaw = typePtr->getPointeeType(); } return typeRaw->isArrayType(); } bool ExternLayer::isRecordType(const std::string& type){ clang::QualType typeRaw = lookupType(type); if (isPointer(typeRaw)) { const clang::PointerType* typePtr = typeRaw->getAs(); typeRaw = typePtr->getPointeeType(); } return typeRaw->isRecordType(); } clang::QualType ExternLayer::lookupType(const std::string& id) { MatchFinder finder; FinderCallbackTypeDecl callbackTypeDecl; auto matcherTypeDecl = typedefDecl(hasName(id)).bind("typename"); finder.addMatcher(matcherTypeDecl, &callbackTypeDecl); finder.matchAST(ast->getASTContext()); assert(! callbackTypeDecl.typeResult.isNull()); return callbackTypeDecl.typeResult; } llvm::Function* ExternLayer::lookupFunction(const std::string& name) { if (__functions.count(name)) { return __functions.at(name); } MatchFinder finder; FinderCallbackFunction callback; auto matcher = functionDecl(hasName(name)).bind("function"); finder.addMatcher(matcher, &callback); finder.matchAST(ast->getASTContext()); if (callback.typeResult.isNull()) { cout << "[External Layer] " << "Unknown function: " << name << endl; assert(false && "Unknown external function"); } const QualType& tyFuncQual = callback.typeResult; llvm::Type *tyRaw = CodeGen::convertTypeForMemory(__codegen->CGM(), tyFuncQual); llvm::FunctionType* tyRawFunc = llvm::dyn_cast(tyRaw); llvm::Function* function = llvm::Function::Create( tyRawFunc, llvm::GlobalValue::ExternalLinkage, name, __llvm->module.get()); __functions.emplace(name, function); return function; } }//end of xreate namespace diff --git a/cpp/src/ExternLayer.h b/cpp/src/ExternLayer.h index 0bae75e..2a0a976 100644 --- a/cpp/src/ExternLayer.h +++ b/cpp/src/ExternLayer.h @@ -1,72 +1,62 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * ExternLayer.h * * Created on: 4/21/15 * Author: pgess */ /** * \file ExternLayer.h * \brief An external C code interaction support */ #ifndef XREATE_EXTERNLAYER_H #define XREATE_EXTERNLAYER_H #include "llvmlayer.h" #include #include #include #include "ast.h" #include "clang/AST/ASTContext.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CodeGenOptions.h" #include "clang/CodeGen/CodeGenABITypes.h" #include "clang/Lex/PreprocessorOptions.h" namespace clang{ class CodeGenerator; } namespace xreate{ -struct ExternData{ - void addLibrary(Atom&& name, Atom&& package); - void addIncludeDecl(Expression&& e); - - std::vector entries; - std::map __dictLibraries; -}; - /** \brief A wrapper over Clang */ class ExternLayer{ public: ExternLayer(LLVMLayer* llvm); - void init(const AST* root); + void init(const AST* ast); llvm::Function* lookupFunction(const std::string& name); clang::QualType lookupType(const std::string& id); std::vector getStructFields(const clang::QualType& ty); llvm::Type* toLLVMType(const clang::QualType& t); bool isPointer(const clang::QualType& t); bool isArrayType(const std::string& type); bool isRecordType(const std::string& type); - static std::vector fetchPackageFlags(const ExternEntry& entry); - static std::vector fetchPackageLibs(const ExternEntry& entry); + static std::vector fetchPackageFlags(const std::string& package); + static std::vector fetchPackageLibs(const std::string& package); private: std::unique_ptr ast; std::unique_ptr __codegen; std::unique_ptr __clang; LLVMLayer* __llvm; - std::vector entries; std::map __functions; - void addExternalData(const std::vector& data); void loadLibraries(std::vector&& libs); }; } #endif //XREATE_EXTERNLAYER_H diff --git a/cpp/src/analysis/cfagraph.cpp b/cpp/src/analysis/cfagraph.cpp index 2e24ca2..1716a5d 100644 --- a/cpp/src/analysis/cfagraph.cpp +++ b/cpp/src/analysis/cfagraph.cpp @@ -1,181 +1,139 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: CFAGraph.cpp * Author: pgess * * Created on June 27, 2016, 2:09 PM */ /** * \file cfagraph.h * \brief Control Flow Analysis(CFA) graph representation */ #include "analysis/cfagraph.h" #include "analysis/utils.h" using namespace xreate::cfa; using namespace std; void CFAGraph::print(std::ostringstream& output) const { - const std::string& atomBinding = Config::get("transcend.bindings.function"); const std::string& atomBindingScope = Config::get("transcend.bindings.scope"); output << endl << "%\t\tStatic analysis: CFA" << endl; output << __outputPrecomputed.str(); - //show function tags - int counterTags = 0; - boost::format formatFunction("function(\"%1%\")."); - boost::format formatBind(atomBinding + "(\"%1%\", %2%)."); - for (auto function : this->__fnNodes.left) { - const auto tags = this->__fnTags.equal_range(function.first); - output << formatFunction % (function.second) << std::endl; - - for (const auto& tag_ : boost::make_iterator_range(tags)) { - const Expression& tag = tag_.second; - - list tagRaw = xreate::analysis::compile(tag); - assert(tagRaw.size() == 1); - - output << formatBind - % (function.second) - % (tagRaw.front()) - << endl; - ++counterTags; - } - } - - if (counterTags == 0) { - output << "%no function tags at all" << endl; - } - //declare scopes boost::format formatScope("scope(0..%1%)."); output << formatScope % (__transcend->getScopesCount() - 1) << std::endl; //show context rules: for (auto rule : this->__contextRules) { output << ContextRule(rule.second).compile(rule.first) << std::endl; }; - //show scope tags: - counterTags = 0; - boost::format formatScopeBind(atomBindingScope + "(%1%, %2%, strong)."); - for (auto entry : this->__scopeTags) { - ScopePacked scopeId = entry.first; - const Expression& tag = entry.second; - list tagRaw = xreate::analysis::compile(tag); - assert(tagRaw.size() == 1); - - output << formatScopeBind % scopeId % (tagRaw.front()) << endl; - ++counterTags; - } - if (counterTags == 0) { - output << "%scope tags: no tags at all" << endl; - } //parent connections //TOTEST CFG parent function boost::format formatFunctionParent("cfa_parent(%1%, function(\"%2%\"))."); for (const auto &relation : this->__parentFnRelation) { const string& function = this->__fnNodes.left.at(relation.right); output << formatFunctionParent % relation.left % function << endl; } //TOTEST CFG parent scope boost::format formatScopeParent("cfa_parent(%1%, scope(%2%))."); for (const auto &relation : this->__parentScopeRelation) { output << formatScopeParent % relation.first % relation.second << endl; } //call connections boost::format formatCall("cfa_call(%1%, \"%2%\")."); for (const auto &relation : this->__callRelations) { const ScopePacked scopeFrom = relation.left; const string& functionTo = this->__fnNodes.left.at(relation.right); output << formatCall % (scopeFrom) % (functionTo) << endl; } //function specializations description boost::format formatSpecializations("cfa_function_specializations(\"%1%\", %2%)."); const list& functions = __transcend->ast->getAllFunctions(); for (auto f : functions) { if (f->guard.isValid()) { list guardRaw = xreate::analysis::compile(f->guard); assert(guardRaw.size() == 1); output << formatSpecializations % (f->getName()) % (guardRaw.front()) << endl; } } } void CFAGraph::addFunctionAnnotations(const std::string& fn, const std::map& tags) { unsigned int fid = registerNodeFunction(fn); for (auto& tag : tags) { __fnTags.emplace(fid, tag.second); } } void CFAGraph::addScopeAnnotations(const ScopePacked& scope, const std::vector& tags) { for (Expression tag : tags) { __scopeTags.emplace(scope, tag); } } void CFAGraph::addContextRules(const ScopePacked& scope, const std::vector& rules) { for (Expression rule : rules) { __contextRules.emplace(scope, rule); } } void CFAGraph::addCallConnection(const ScopePacked& callerScope, const std::string& calleeFn) { unsigned int idFuncTo = registerNodeFunction(calleeFn); __callRelations.insert(CALL_RELATIONS::value_type(callerScope, idFuncTo)); } void CFAGraph::addParentConnection(const ScopePacked& scopeEntry, const std::string& fnParent) { __parentFnRelation.insert(PARENT_FUNCTION_RELATIONS::value_type(scopeEntry, registerNodeFunction(fnParent))); } void CFAGraph::addParentConnection(const ScopePacked& scopeChild, const ScopePacked& scopeParent) { __parentScopeRelation.emplace(scopeChild, scopeParent); } unsigned int CFAGraph::registerNodeFunction(const std::string& fname) { auto pos = __fnNodes.left.insert(make_pair(__fnNodes.size(), fname)); return pos.first->first; } void CFAGraph::addScope(CodeScope* scope) { boost::format formatScopeBinding("ast_scope_binding(%1%, %2%, \"%3%\")."); ScopePacked scopeId = __transcend->pack(scope); __scopesCount = max(scopeId + 1, __scopesCount); for (int id = 0, size = scope->__bindings.size(); id < size; ++id) { __outputPrecomputed << formatScopeBinding % scopeId % id % scope->__bindings.at(id) << endl; } } diff --git a/cpp/src/analysis/interpretation.cpp b/cpp/src/analysis/interpretation.cpp deleted file mode 100644 index 7c18b4e..0000000 --- a/cpp/src/analysis/interpretation.cpp +++ /dev/null @@ -1,269 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Author: pgess - * Created on June 25, 2018, 3:25 PM - */ - -#include "analysis/interpretation.h" - -using namespace std; - -namespace xreate{ -namespace interpretation{ - -typedef vector InstancePacked; - -std::list -generateAllInstancesInDomain2(const ExpandedType& domainT) { - if(!domainT->isValid()) { - return {Expression()}; - } - assert(domainT->__operator == TypeOperator::VARIANT); - std::list results; - int variantId = -1; - - bool flagDomainStateless = std::all_of(domainT->__operands.begin(), domainT->__operands.end(), - [](const TypeAnnotation & subdomainT) { - return !subdomainT.isValid(); - }); - - for(const TypeAnnotation& subdomainT : domainT->__operands) { - ++variantId; - - if(flagDomainStateless) { - Expression result(Operator::VARIANT,{}); - result.setValueDouble(variantId); - results.push_back(result); - continue; - } - - std::list subresults = generateAllInstancesInDomain2(ExpandedType(subdomainT)); - for (const Expression& subresult : subresults) { - Expression result(Operator::VARIANT,{}); - result.setValueDouble(variantId); - result.operands.push_back(subresult); - - results.push_back(result); - } - } - - return results; -} - -TypeAnnotation -collapseFnGroup(const std::list& symbols) { - Gringo::Symbol symbolAny = symbols.front(); - size_t operandsCount = symbolAny.args().size; - - TypeAnnotation resultT; - resultT.__operands.reserve(operandsCount); - - for(size_t operandId = 0; operandId < operandsCount; ++operandId) { - std::list column; - - for(const Gringo::Symbol& row : symbols) { - column.push_back(row.args()[operandId]); - } - - TypeAnnotation operandT = collapseColumn(column); - resultT.__operands.push_back(operandT); - } - - if(resultT.__operands.size() == 1) { - return resultT.__operands.front(); - } - - if(resultT.__operands.size() > 1) { - resultT.__operator = TypeOperator::LIST_RECORD; - return resultT; - } - - return resultT; -} - -TypeAnnotation -collapseColumn(const std::list& symbols) { - TypeAnnotation resultT; - if(!symbols.size()) return resultT; - - Gringo::Symbol symbolAny = symbols.front(); - - switch(symbolAny.type()) { - case Gringo::SymbolType::Num: - { - return TypeAnnotation(TypePrimitive::Num); - } - - case Gringo::SymbolType::Str: - { - return TypeAnnotation(TypePrimitive::String); - } - - case Gringo::SymbolType::Fun: - { - map> fnGroups; - - for(const Gringo::Symbol& row : symbols) { - fnGroups[row.name().c_str()].push_back(row); - } - - TypeAnnotation resultT; - resultT.__operands.reserve(fnGroups.size()); - resultT.bindings.reserve(fnGroups.size()); - - for(const auto& group : fnGroups) { - if(!group.second.size()) continue; - - TypeAnnotation variantT = collapseFnGroup(group.second); - Gringo::Symbol symbolAny = group.second.front(); - string variantName = symbolAny.name().c_str(); - resultT.fields.push_back(variantName); - resultT.__operands.push_back(variantT); - } - - resultT.__operator = TypeOperator::VARIANT; - // if(resultT.__operands.size() == 1) { - // return resultT.__operands.front(); - // } - return resultT; - } - - case Gringo::SymbolType::Inf: - case Gringo::SymbolType::Special: - case Gringo::SymbolType::Sup: - { - break; - } - } - - assert(false); - return TypeAnnotation(); -} - -ExpandedType -dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend) { - assert(t->__operator == TypeOperator::SLAVE); - const string& domain = t->__valueCustom; - StaticModel model = transcend->query(domain); - if(!model.size()) return ExpandedType(TypeAnnotation()); - - std::list symbols; - for(auto row : model) { - symbols.push_back(row.second); - } - return ExpandedType(collapseFnGroup(symbols)); -} - -Expression -representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend) { - atom.print(std::cout); std::cout<__operator) { - case TypeOperator::NONE: - { - switch(schemaT->__value) { - case TypePrimitive::I8: - case TypePrimitive::I32: - case TypePrimitive::I64: - case TypePrimitive::Num: - case TypePrimitive::Int: - { - return Expression(Atom(atom.num())); - } - - case TypePrimitive::String: - { - return Expression(Atom(atom.string().c_str())); - } - - case TypePrimitive::Invalid: - case TypePrimitive::Bool: - case TypePrimitive::Float: - { - assert(false); - return Expression(); - } - } - break; - } - - case TypeOperator::SLAVE: - { - ExpandedType contentT = dereferenceSlaveType(schemaT, transcend); - return representTransExpression(atom, contentT, transcend); - } - - case TypeOperator::VARIANT: - { - map dictVariants; - for(size_t variantId = 0; variantId < schemaT->fields.size(); ++variantId) { - dictVariants.emplace(schemaT->fields.at(variantId), variantId); - } - - string predicateName = atom.name().c_str(); - assert(dictVariants.count(predicateName)); - - size_t predicateId = dictVariants.at(predicateName); - Expression result(Operator::VARIANT,{}); - result.op = Operator::VARIANT; - result.setValueDouble(predicateId); - - if(!schemaT->__operands.size()) return result; - ExpandedType contentT = schemaT->__operands.at(predicateId).__operator == TypeOperator::SLAVE - ? dereferenceSlaveType(ExpandedType(schemaT->__operands.at(predicateId)), transcend) - : ExpandedType(schemaT->__operands.at(predicateId)); - - //edge case, content's type is LIST_NAMED: - if (contentT->__operator == TypeOperator::LIST_RECORD) { - result.operands.push_back(representTransExpression(atom, contentT, transcend)); - - } else if(!contentT->isValid()) { - return result; - - } else { - assert(atom.args().size); - result.operands.push_back(representTransExpression(atom.args()[0], contentT, transcend)); - } - - return result; - } - - case TypeOperator::LIST_RECORD: - { - const Gringo::SymSpan& operandsRaw = atom.args(); - size_t opCount = operandsRaw.size; - assert(opCount == schemaT->__operands.size()); - - size_t operandId = 0; - std::vector operands; - operands.reserve(opCount); - for(const TypeAnnotation operandT : schemaT->__operands) { - operands.push_back(representTransExpression(operandsRaw[operandId], ExpandedType(operandT), transcend)); - ++operandId; - } - - Expression result(Operator::LIST,{}); - result.operands = operands; - result.type = schemaT; - return result; - } - - case TypeOperator::LIST_ARRAY: - case TypeOperator::CALL: - case TypeOperator::CUSTOM: - case TypeOperator::ACCESS: - case TypeOperator::LINK: - { - assert(false); - return Expression(); - } - } - - assert(false); - return Expression(); -} - -} -} //end of xreate namespace diff --git a/cpp/src/analysis/interpretation.h b/cpp/src/analysis/interpretation.h deleted file mode 100644 index 8a93e17..0000000 --- a/cpp/src/analysis/interpretation.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Author: pgess - * Created on June 25, 2018, 3:24 PM - */ - -/** - * \file interpretation.h - * \brief Interpretation related functions - */ - -#ifndef INTERPRETATION_H -#define INTERPRETATION_H - -#include "transcendlayer.h" - -namespace xreate{ namespace interpretation{ - - /** - * \brief Converts a Transcend's fact to a Xreate's expression that can be interpreted further. Supports `query` keyword. - * @param atom Transcend's fact - * @param schemaT Type of the resulting expression - * @param transcend Transcend's instance - * @return converted Transcend' fact in form of Xreate's expression - */ - Expression representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend); - - /** - * \brief Expands slave type. - * @param t Slave type - * @param transcend Instance of Transcend - * @return The expanded slave type - */ - ExpandedType dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend); - - TypeAnnotation collapseColumn(const std::list& symbols); - std::list generateAllInstancesInDomain2(const ExpandedType& domainT); - -} -} -#endif /* INTERPRETATION_H */ - diff --git a/cpp/src/analysis/predefinedanns.cpp b/cpp/src/analysis/predefinedanns.cpp new file mode 100644 index 0000000..59812a2 --- /dev/null +++ b/cpp/src/analysis/predefinedanns.cpp @@ -0,0 +1,64 @@ +// +// Created by pgess on 19/03/2020. +// + +#include "analysis/predefinedanns.h" + +namespace xreate{ namespace analysis{ + +PredefinedAnns +PredefinedAnns::__instance = PredefinedAnns(); + +PredefinedAnns::PredefinedAnns(){ + fnAnnsT = TypeAnnotation(TypeOperator::VARIANT, {}); + exprAnnsT = TypeAnnotation(TypeOperator::VARIANT, {}); + + //entry + fnAnnsT.__operands.push_back(TypePrimitive::Invalid); + fnAnnsT.fields.push_back("entry"); + + //interpretation + i12nModeT = TypeAnnotation(TypeOperator::VARIANT, { + TypePrimitive::Invalid, + TypePrimitive::Invalid + }); + i12nModeT.fields.push_back("on"); + i12nModeT.fields.push_back("off"); + + exprAnnsT.__operands.push_back(i12nModeT); + exprAnnsT.fields.push_back("i12n"); + + //Int + hintsIntT = TypeAnnotation(TypeOperator::VARIANT, { + TypePrimitive ::Int + }); + hintsIntT.fields.push_back("size"); + + //Containers + hintsContT = TypeAnnotation(TypeOperator::VARIANT, { + TypePrimitive::Int + }); + hintsContT.fields.push_back("csize"); +} + +void +PredefinedAnns::fillRegistry(std::map> &__registry) const{ + __registry = { + //Functions: + {"entry", {fnAnnsT, (unsigned) FnAnnotations::ENTRY}}, + + //Expressions: + {"i12n", {exprAnnsT, (unsigned) ExprAnnotations::I12N}}, + + //Interpretation: + {"on", {i12nModeT, (unsigned) I12ModeTag::ON}}, + {"off", {i12nModeT, (unsigned) I12ModeTag::OFF}}, + + //Int: + {"size", {hintsIntT, (unsigned) IntHints::SIZE}}, + + //Containers + {"csize", {hintsContT, (unsigned) ContHints::ARRAY}}, + }; +} +}} \ No newline at end of file diff --git a/cpp/src/analysis/predefinedanns.h b/cpp/src/analysis/predefinedanns.h new file mode 100644 index 0000000..6d16e73 --- /dev/null +++ b/cpp/src/analysis/predefinedanns.h @@ -0,0 +1,48 @@ +// +// Created by pgess on 19/03/2020. +// + +#ifndef XREATE_PREDEFINEDANNS_H +#define XREATE_PREDEFINEDANNS_H + +#include "ast.h" + +namespace xreate{ namespace analysis{ +class PredefinedAnns{ +public: + enum class FnAnnotations{ + ENTRY + }; + enum class ExprAnnotations{ + I12N + }; + enum class I12ModeTag{ + ON, OFF + }; + enum class IntHints{ + SIZE + }; + + enum class ContHints{ + ARRAY + }; + + TypeAnnotation fnAnnsT; + TypeAnnotation exprAnnsT; + TypeAnnotation hintsIntT; + TypeAnnotation hintsContT; + + //Interpretation + TypeAnnotation i12nModeT; + + PredefinedAnns(); + static const PredefinedAnns& instance(){ return __instance; } + void fillRegistry(std::map> &__registry) const; + +private: + static PredefinedAnns __instance; +}; + +}} // end of xreate::analysis + +#endif //XREATE_PREDEFINEDANNS_H diff --git a/cpp/src/analysis/resources.cpp b/cpp/src/analysis/resources.cpp new file mode 100644 index 0000000..e80a121 --- /dev/null +++ b/cpp/src/analysis/resources.cpp @@ -0,0 +1,24 @@ +#include "analysis/resources.h" +namespace xreate{namespace analysis{ + +const char *VAR_ANN_PREDICATE_TPL = "bind(%1%, %2%)"; +const char *VAR_ANN_PREDICATE = "bind"; +const char *SCOPE_ANN_PREDICATE_TPL = "bind_scope(%1%, %2%)"; +const char *SCOPE_ANN_PREDICATE = "bind_scope"; +const char *FUNCTION_ANN_PREDICATE_TPL = "bind_func(\"%1%\", %2%)"; +const char *FUNCTION_ANN_PREDICATE = "bind_func"; + +const char *TRANSCEND_PASS_SECTION = "% == STATIC ANALYSIS: ANNOTATIONS =="; +const char *FUNCTION_PREDICATE_TPL = "function(\"%1%\")"; +const char *SITE_SYMBOL_PREDICATE = "s"; +const char *SITE_ANON_PREDICATE = "a"; +const char *DEMAND_FORMAL_PREDICATE = "func_demand"; +const char *DEMAND_ACTUAL_PREDICATE = "func_supply"; +const char *FN_ENTRY_PREDICATE = "entry"; +const char *POLYMORPH_SUPPLY_PREDICATE = "func_supply_guard"; + +const char *CONTAINERS_ID_IMPL_PREDICATE = "containers_impl"; +const char *CONTAINERS_ID_LINKLIST_PREDICATE = "linkedlist"; +const char *CONTAINERS_IMPL_SOLID_PREDICATE = "solid"; +const char *CONTAINERS_IMPL_ONTHEFLY_PREDICATE = "onthefly"; +}} \ No newline at end of file diff --git a/cpp/src/analysis/resources.h b/cpp/src/analysis/resources.h new file mode 100644 index 0000000..6371c0a --- /dev/null +++ b/cpp/src/analysis/resources.h @@ -0,0 +1,37 @@ +// +// Created by pgess on 15/01/2020. +// +#ifndef XREATE_RESOURCES_H +#define XREATE_RESOURCES_H +#include "transcendlayer.h" +#include +#include + +namespace xreate{namespace analysis { +extern const char *FUNCTION_PREDICATE_TPL; +extern const char *FUNCTION_ANN_PREDICATE_TPL; +extern const char *FUNCTION_ANN_PREDICATE; +extern const char *SCOPE_ANN_PREDICATE_TPL; +extern const char *SCOPE_ANN_PREDICATE; +extern const char *VAR_ANN_PREDICATE_TPL; +extern const char *VAR_ANN_PREDICATE; +extern const char *TRANSCEND_PASS_SECTION; +extern const char *SITE_SYMBOL_PREDICATE; +extern const char *SITE_ANON_PREDICATE; +extern const char *DEMAND_FORMAL_PREDICATE; +extern const char *DEMAND_ACTUAL_PREDICATE; +extern const char *POLYMORPH_SUPPLY_PREDICATE; +extern const char *FN_ENTRY_PREDICATE; + +extern const char *CONTAINERS_ID_IMPL_PREDICATE; +extern const char *CONTAINERS_ID_LINKLIST_PREDICATE; +extern const char *CONTAINERS_IMPL_SOLID_PREDICATE; +extern const char *CONTAINERS_IMPL_ONTHEFLY_PREDICATE; + +typedef std::tuple DEMAND_FORMAL_SCHEME; //fn, key, type-alias +typedef std::tuple DEMAND_ACTUAL_SCHEME; //fn, key, type-alias +typedef std::tuple POLYMORPH_SUPPLY_SCHEME; //site, data, type-variant + +}} + +#endif //XREATE_RESOURCES_H diff --git a/cpp/src/analysis/transcendtarget.cpp b/cpp/src/analysis/transcendtarget.cpp new file mode 100644 index 0000000..7b4015c --- /dev/null +++ b/cpp/src/analysis/transcendtarget.cpp @@ -0,0 +1,187 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: pgess + * Created on: Jan, 2020 +*/ + +#include "transcendtarget.h" +#include "analysis/utils.h" +#include "analysis/resources.h" + +#include +#include +using namespace std; + +namespace xreate{ + +void +TranscendPass::process(ManagedFnPtr function){ + boost::format formatFn(analysis::FUNCTION_PREDICATE_TPL); + boost::format formatFnAnn(analysis::FUNCTION_ANN_PREDICATE_TPL); + + __output << endl << analysis::TRANSCEND_PASS_SECTION << endl; + __output << formatFn + % (function->getName()) + << "." << std::endl; + + for(const auto &tagRec: function->getTags()){ + const Expression &tag = tagRec.second; + + TranscendScope* scopeTr = getRootScope(); + list definitions; + const string& partTagRaw = scopeTr->compile(tag, definitions); + TranscendScope::rule(__output, + boost::str(formatFnAnn + % (function->getName()) + % (partTagRaw)), + definitions); + + __output << endl; + } + + return Parent::process(function); +} + +void +TranscendPass::process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl) { + TranscendLayer* transcend = man->transcend; + boost::format formatScopeAnn(analysis::SCOPE_ANN_PREDICATE_TPL); + ScopePacked scopeId = transcend->pack(scope); + TranscendScope* scopeTr = getScope(scope); + + for (const auto& tag : scope->tags){ + list definitions; + const string& partTagRaw = scopeTr->compile(tag, definitions); + TranscendScope::rule(__output, + boost::str(formatScopeAnn + % scopeId + % (partTagRaw)), + definitions); + + __output << endl; + } + + return Parent::process(scope, context, hintBlockDecl); +} + +void +TranscendPass::process(const Expression &expression, PassContext context, const std::string &varDecl){ + boost::format formatVarAnn(analysis::VAR_ANN_PREDICATE_TPL); + TranscendLayer* transcend = man->transcend; + TranscendScope* scopeTr = getScope(context.scope); + + for (const auto& tag: expression.tags){ + list body; + const string& head = scopeTr->compile(tag.second, body); + + TranscendScope::rule(__output, + boost::str(formatVarAnn + % Parent::man->transcend->pack(ASTSite{expression.id}).str() + % head), body); + } + + return Parent::process(expression, context, varDecl); +} + +TranscendScope* +TranscendPass::getScope(const CodeScope * const scope){ + if(__scopes.count(scope)){ + return __scopes.at(scope); + } + + TranscendScope* unit(new TranscendScope(scope, this)); + __scopes[scope] = unit; + return unit; +} + +void +TranscendPass::finish(){ + Parent::man->transcend->addRawScript(__output.str()); +} + +std::string +TranscendScope::compile(const Expression &e, std::list& definitions){ + switch(e.__state){ + case Expression::COMPOUND: { + switch (e.op){ + case Operator::VARIANT: { + const string& predicate = escapePredicate(string(e.getValueString())); + + if(!e.operands.size()){ + return predicate; + } + + list operands; + std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()), + [this, &definitions](const Expression &e) { + return this->compile(e, definitions); + }); + + return boost::str( + boost::format("%1%(%2%)") + % predicate + % boost::algorithm::join(operands, ", ")); + } + default: { + return analysis::getSite(e, __pass->man->transcend).str(); + } + } + } + + case Expression::NUMBER: { + return to_string((unsigned int)e.getValueDouble()); + break; + } + + case Expression::STRING: { + return boost::str(boost::format("\"%1%\"") % e.getValueString()); + } + + case Expression::IDENT: { + Symbol s = Attachments::get(e); + const string& identSafe = escapeVar(string(e.getValueString())); + const Expression& symbDefinition = CodeScope::getDefinition(s); + definitions.push_back(boost::str(boost::format("%1% = %2%") + % identSafe + % compile(symbDefinition, definitions) + )); + + return identSafe; + } + + case Expression::INVALID: case Expression::BINDING: assert(false); + } + + assert(false); + return ""; +} + +std::string +TranscendScope::escapeVar(std::string&& var){ + assert(var.size()); + + var[0] = std::toupper(var[0]); + return var; +} + +std::string +TranscendScope::escapePredicate(std::string&& pred){ + assert(pred.size()); + + pred[0] = std::tolower(pred[0]); + return pred; +} + +void +TranscendScope::rule(std::ostringstream& output, const std::string& head, const std::list& body){ + output << head; + + const string& partBody = boost::join(body, ";"); + if (!partBody.empty()){ + output << " :- " << partBody; + } + output << "."; +} +} \ No newline at end of file diff --git a/cpp/src/analysis/transcendtarget.h b/cpp/src/analysis/transcendtarget.h new file mode 100644 index 0000000..9cb4775 --- /dev/null +++ b/cpp/src/analysis/transcendtarget.h @@ -0,0 +1,87 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: pgess + * Created on: Jan, 2020 +*/ +#ifndef XREATE_TRANSCENDTARGET_H +#define XREATE_TRANSCENDTARGET_H + +#include "pass/abstractpass.h" +#include "transcendlayer.h" + +namespace xreate{ +//struct TranscendTag{}; +//class TranscendScope; +//class TranscendFn; +//class TranscendTarget; +// +//typedef std::list TranscendResult; +// +//template<> +//struct compilation::TargetInfo{ +//public: +// typedef TranscendResult Result; +// typedef TranscendFn Function; +// typedef TranscendScope Scope; +//}; +// +//class TranscendScope: public compilation::Scope{ +// typedef compilation::Scope Parent; +// +//public: +// TranscendScope(const CodeScope* codeScope, compilation::Function* f): Parent(codeScope, f){} +// TranscendResult processMetaSymbol(const Symbol& s); +// TranscendResult processMetaExpression(const Expression& expression); +// +// virtual TranscendResult process(const Expression& expression) override; +//}; +// +//class TranscendFn: public compilation::Function{ +// typedef compilation::Function Parent; +// +//public: +// TranscendFn(const ManagedFnPtr& fn, compilation::Target* target): Parent(fn, target){} +// virtual TranscendResult process(const std::vector& args) override; +//}; +// +//class TranscendTarget: public compilation::Target{ +//public: +// virtual void run() override; +//}; + +class TranscendPass; + +class TranscendScope { +public: + TranscendScope(const CodeScope* scope, const TranscendPass* pass): __pass(pass){} + std::string compile(const Expression& e, std::list& definitions); + static void rule(std::ostringstream& output, const std::string& head, const std::list& body); + +private: + const TranscendPass* __pass; + std::string escapeVar(std::string&& var); + std::string escapePredicate(std::string&& pred); + +}; + +class TranscendPass: public AbstractPass{ + typedef AbstractPass Parent; + +public: + TranscendPass(PassManager* manager): Parent(manager){} + void process(ManagedFnPtr function) override; + void process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl = "") override; + void process(const Expression &expression, PassContext context, const std::string &varDecl = "") override; + void finish() override; + +private: + std::map __scopes; + std::ostringstream __output; + + TranscendScope* getScope(const CodeScope * const scope); + TranscendScope* getRootScope() {return getScope(0); } +}; +} +#endif //XREATE_TRANSCENDTARGET_H diff --git a/cpp/src/analysis/typehints.cpp b/cpp/src/analysis/typehints.cpp new file mode 100644 index 0000000..a0a6603 --- /dev/null +++ b/cpp/src/analysis/typehints.cpp @@ -0,0 +1,52 @@ +// +// Created by pgess on 24/03/2020. +// + +#include "analysis/typehints.h" +#include "analysis/predefinedanns.h" +#include "analysis/utils.h" + +namespace xreate{namespace typehints{ + +namespace impl { +template +inline Hint getHint(const Expression& e, const Hint& def, unsigned annId, const ExpandedType& hintT){ + std::list hintsL; + auto predefined = analysis::PredefinedAnns::instance(); + for(const auto& tag: e.tags){hintsL.push_back(tag.second);} + const Expression& hintActual = analysis::findAnnById(annId, hintT, hintsL); + if (!hintActual.isValid()){ + return def; + } + + return parse(hintActual); +} +} + +template<> IntBits parse(const Expression& e){ + return {(unsigned) e.operands.at(0).getValueDouble()}; +} + +template<> Array parse(const Expression& e){ + return {(unsigned) e.operands.at(0).getValueDouble()}; +} + +template<> +IntBits getHint(const Expression& e, const IntBits& def){ + auto predefined = analysis::PredefinedAnns::instance(); + return impl::getHint(e, def, + (unsigned) analysis::PredefinedAnns::IntHints::SIZE, + ExpandedType(predefined.hintsIntT) + ); +} + +template<> +Array getHint(const Expression& e, const Array& def){ + auto predefined = analysis::PredefinedAnns::instance(); + return impl::getHint(e, def, + (unsigned) analysis::PredefinedAnns::ContHints::ARRAY, + ExpandedType(predefined.hintsContT) + ); +} + +}} \ No newline at end of file diff --git a/cpp/src/analysis/typehints.h b/cpp/src/analysis/typehints.h new file mode 100644 index 0000000..67028cc --- /dev/null +++ b/cpp/src/analysis/typehints.h @@ -0,0 +1,46 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * typehints.h + * + * Author: pgess + */ + +/** + * \file typehints.h + * \brief Type inference hints + */ +#ifndef XREATE_TYPEHINTS_H +#define XREATE_TYPEHINTS_H + +#include "ast.h" + +namespace xreate{namespace typehints{ + +struct IntBits{ + unsigned int size; +}; + +struct Array{ + size_t size; +}; + +template +Typ getHint(const Expression& e, const Typ& def); + +template<> +IntBits getHint(const Expression& e, const IntBits& def); + +template<> +Array getHint(const Expression& e, const Array& def); + +template +Typ parse(const Expression& e); + +template<> IntBits parse(const Expression& e); +template<> Array parse(const Expression& e); + +}} + +#endif //XREATE_TYPEHINTS_H diff --git a/cpp/src/analysis/typeinference.cpp b/cpp/src/analysis/typeinference.cpp index c6d2b5a..fce6d88 100644 --- a/cpp/src/analysis/typeinference.cpp +++ b/cpp/src/analysis/typeinference.cpp @@ -1,98 +1,114 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * typeinference.cpp * * Author: pgess * Created on April 16, 2017, 10:13 AM */ /** * \file typeinference.h * \brief Type inference analysis */ #include "typeinference.h" #include "llvmlayer.h" #include "transcendlayer.h" #include "llvm/IR/Function.h" #include "llvm/IR/DerivedTypes.h" using namespace std; namespace xreate{ namespace typeinference{ //TODO type conversion: //a) automatically expand types int -> bigger int; int -> floating //b) detect exact type of `num` based on max used numeral / function type //c) warning if need to truncate (allow/dissallow based on annotations) llvm::Value* doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder) { - if(tyTarget->isIntegerTy() && source->getType()->isIntegerTy()) { - llvm::IntegerType* tyTargetInt = llvm::dyn_cast(tyTarget); - llvm::IntegerType* tySourceInt = llvm::dyn_cast(source->getType()); - - if(tyTargetInt->getBitWidth() < tySourceInt->getBitWidth()) { - return builder.CreateCast(llvm::Instruction::Trunc, source, tyTarget); - } - - if(tyTargetInt->getBitWidth() > tySourceInt->getBitWidth()) { - return builder.CreateCast(llvm::Instruction::SExt, source, tyTarget); - } - } - - if(source->getType()->isIntegerTy() && tyTarget->isFloatingPointTy()) { - return builder.CreateCast(llvm::Instruction::SIToFP, source, tyTarget); - } - - if (source->getType()->isStructTy() && tyTarget->isIntegerTy()){ - llvm::StructType* sourceST = llvm::cast(source->getType()); - if(sourceST->getNumElements() == 1) { - llvm::Value* sourceElRaw = builder.CreateExtractValue(source, llvm::ArrayRef({0})); - return doAutomaticTypeConversion(sourceElRaw, tyTarget, builder); - } - } - - return source; + if (!tyTarget) return source; + if(tyTarget->isIntegerTy() && source->getType()->isIntegerTy()) { + llvm::IntegerType* tyTargetInt = llvm::dyn_cast(tyTarget); + llvm::IntegerType* tySourceInt = llvm::dyn_cast(source->getType()); + + if(tyTargetInt->getBitWidth() < tySourceInt->getBitWidth()) { + return builder.CreateCast(llvm::Instruction::Trunc, source, tyTarget); + } + + if(tyTargetInt->getBitWidth() > tySourceInt->getBitWidth()) { + return builder.CreateCast(llvm::Instruction::SExt, source, tyTarget); + } + } + + if(source->getType()->isIntegerTy() && tyTarget->isFloatingPointTy()) { + return builder.CreateCast(llvm::Instruction::SIToFP, source, tyTarget); + } + + if (source->getType()->isStructTy() && tyTarget->isIntegerTy()){ + llvm::StructType* sourceST = llvm::cast(source->getType()); + if(sourceST->getNumElements() == 1) { + llvm::Value* sourceElRaw = builder.CreateExtractValue(source, llvm::ArrayRef({0})); + return doAutomaticTypeConversion(sourceElRaw, tyTarget, builder); + } + } + + return source; } /** * \brief Performs basic type inference to deduce the type of the given expression * * Tries several strategies in the following order: * - Looks at expression's type if it has one. * - Looks at Attachment if it has one. This allows assign expression's type by analyses done elsewhere. * - For a number literal assumes i32. * * \param expression Infers the given expression's type. * \param ast AST instance. */ ExpandedType -getType(const Expression& expression, const AST& ast) { - if(expression.type.isValid()) { - return ast.expandType(expression.type); - } +getType(const Expression& expression, const TypeAnnotation& expectedT, const AST& ast) { + if(expression.type.isValid()) { + return ast.expandType(expression.type); + } - if(expression.__state == Expression::IDENT) { - Symbol s = Attachments::get(expression); - return getType(CodeScope::getDefinition(s), ast); - } + if(expectedT.isValid()){ + return ast.expandType(expectedT); + } - if(Attachments::exists(expression)) { - return Attachments::get(expression); - } + if(Attachments::exists(expression)) { + return Attachments::get(expression); + } - if(expression.__state == Expression::NUMBER) { - return ExpandedType(TypeAnnotation(TypePrimitive::I32)); - } + if(expression.__state == Expression::IDENT) { + Symbol s = Attachments::get(expression); + return getType(CodeScope::getDefinition(s), TypeAnnotation(), ast); + } + + if(expression.__state == Expression::NUMBER) { + return ExpandedType(TypeAnnotation(TypePrimitive::Int)); + } + assert(false && "Type can't be determined for an expression"); +} + +ExpandedType +getSubtype(const ExpandedType& type, const std::string& index){ + for(size_t idx = 0; idx < type->fields.size(); ++idx){ + if (index == type->fields.at(idx)){ + return ExpandedType(type->__operands.at(idx)); + } + } - assert(false && "Type can't be determined for an expression"); + assert(false); + return ExpandedType(TypeAnnotation()); } } } //end of namespace xreate::typeinference diff --git a/cpp/src/analysis/typeinference.h b/cpp/src/analysis/typeinference.h index 1990995..f7207e9 100644 --- a/cpp/src/analysis/typeinference.h +++ b/cpp/src/analysis/typeinference.h @@ -1,36 +1,37 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: typeinference.h * Author: pgess * * Created on April 16, 2017, 10:17 AM */ #ifndef TYPEINFERENCE_H #define TYPEINFERENCE_H #include "ast.h" #include "llvm/IR/IRBuilder.h" namespace llvm{ class Value; class Type; }; namespace xreate{ namespace typeinference{ /** * \brief Casts the given variable to the given type * \param source The variable that needs casting * \param tyTarget The type to cast to * \param builder Instance of llvm's IRBuilder */ llvm::Value* doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder); -ExpandedType getType(const Expression& expression, const AST& ast); +ExpandedType getType(const Expression& expression, const TypeAnnotation& expectedT, const AST& ast); +ExpandedType getSubtype(const ExpandedType& type, const std::string& index); } }//namespace xreate::typeinference #endif /* TYPEINFERENCE_H */ diff --git a/cpp/src/analysis/utils.cpp b/cpp/src/analysis/utils.cpp index 1d8df75..a7e13bd 100644 --- a/cpp/src/analysis/utils.cpp +++ b/cpp/src/analysis/utils.cpp @@ -1,159 +1,425 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * aux.cpp * * Author: pgess */ /** * \file src/analysis/utils.h * \brief Various reasoning related utilities */ #include "utils.h" +#include "resources.h" #include using namespace std; namespace xreate { namespace analysis { -std::list -compile(const Expression &e){ - list result; - - switch (e.op) { - case Operator::CALL: { - if(!e.operands.size()){ - result.push_back(e.getValueString()); - break; - } - - std::list> operands; - std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()), - [](const Expression &e) { - return compile(e); - }); - - list &&operands_ = multiplyLists(std::move(operands)); - result.push_back(boost::str(boost::format("%1%(%2%)") % (e.getValueString()) % (boost::algorithm::join(operands_, ", ")))); - break; - } +//std::list multiplyLists(std::list> &&lists){ +// assert(false); +//} +// +//std::list +//compile(const Expression &e){ +// list result; +// +// switch (e.op) { +// case Operator::CALL: { +// +// } +// +// case Operator::NEG: { +// assert(e.operands.size() == 1); +// +// const Expression &op = e.operands.at(0); +// list &&rawOp = compile(op); +// +// assert(rawOp.size() == 1); +// result.push_back((boost::format("not %1%")%(rawOp.front())).str()); +// break; +// }; +// +// case Operator::INVALID: { +// switch (e.__state) { +// case Expression::IDENT: +// result.push_back(e.getValueString()); +// break; +// +// case Expression::NUMBER: +// result.push_back(to_string(e.getValueDouble())); +// break; +// +// default: +// assert(true); +// } +// break; +// } +// +// default: break; +// } +// +// assert(result.size()); +// return result; +//} +// +//std::list +//compileNeg(const Expression &e){ +// list result; +// switch (e.op) { +// case Operator::IMPL: { +// assert(e.__state == Expression::COMPOUND); +// assert(e.operands.size() == 2); +// list operands1 = compile(e.operands.at(0)); +// list operands2 = compile(e.operands.at(1)); +// +// boost::format formatNeg("%1%, not %2%"); +// for (const auto &op1: operands1) +// for (const auto &op2: operands2) { +// result.push_back(boost::str(formatNeg %(op1) % (op2))); +// } +// break; +// } +// case Operator::NEG: { +// assert(e.operands.size() == 1); +// +// const Expression &op = e.operands.at(0); +// list &&rawOp = compile(op); +// +// assert(rawOp.size() == 1); +// result.push_back(rawOp.front()); +// break; +// }; +// +// default: +// assert(true); +// } +// +// return result; +//} +// +////NOTE: Any changes should be reflected in ParseImplAtom, +//// ParseImplAtom +//class VisitorFormatSymbol: public boost::static_visitor { +//public: +// +// boost::format operator()(const SymbolPacked& node) const { +// boost::format formatSymbNamed("s(%1%,%2%,%3%)"); +// return formatSymbNamed % node.identifier % node.version % node.scope ; +// } +// +// boost::format operator()(const SymbolAnonymous& node) const { +// boost::format formatSymbAnonymous("a(%1%)"); +// return formatSymbAnonymous % node.id; +// } +//}; +// +//boost::format writeSymbolNode(const SymbolNode& symbol){ +// return boost::apply_visitor(VisitorFormatSymbol(), symbol); +//} + +typedef vector InstancePacked; + +std::list +generateAllInstancesInDomain2(const ExpandedType& domainT) { + if(!domainT->isValid()) { + return {Expression()}; + } + assert(domainT->__operator == TypeOperator::VARIANT); + std::list results; + int variantId = -1; + + bool flagDomainStateless = std::all_of(domainT->__operands.begin(), domainT->__operands.end(), + [](const TypeAnnotation & subdomainT) { + return !subdomainT.isValid(); + }); + + for(const TypeAnnotation& subdomainT : domainT->__operands) { + ++variantId; + + if(flagDomainStateless) { + Expression result(Operator::VARIANT,{}); + result.setValueDouble(variantId); + results.push_back(result); + continue; + } - case Operator::NEG: { - assert(e.operands.size() == 1); + std::list subresults = generateAllInstancesInDomain2(ExpandedType(subdomainT)); + for (const Expression& subresult : subresults) { + Expression result(Operator::VARIANT,{}); + result.setValueDouble(variantId); + result.operands.push_back(subresult); - const Expression &op = e.operands.at(0); - list &&rawOp = compile(op); + results.push_back(result); + } + } - assert(rawOp.size() == 1); - result.push_back((boost::format("not %1%")%(rawOp.front())).str()); - break; - }; + return results; +} - case Operator::INVALID: { - switch (e.__state) { - case Expression::IDENT: - result.push_back(e.getValueString()); - break; +TypeAnnotation +collapseFnGroup(const std::list& symbols) { + Gringo::Symbol symbolAny = symbols.front(); + size_t operandsCount = symbolAny.args().size; - case Expression::NUMBER: - result.push_back(to_string(e.getValueDouble())); - break; + TypeAnnotation resultT; + resultT.__operands.reserve(operandsCount); - default: - assert(true); - } - break; - } + for(size_t operandId = 0; operandId < operandsCount; ++operandId) { + std::list column; - default: break; + for(const Gringo::Symbol& row : symbols) { + column.push_back(row.args()[operandId]); } - assert(result.size()); - return result; + TypeAnnotation operandT = collapseColumn(column); + resultT.__operands.push_back(operandT); + } + + if(resultT.__operands.size() == 1) { + return resultT.__operands.front(); + } + + if(resultT.__operands.size() > 1) { + resultT.__operator = TypeOperator::RECORD; + return resultT; + } + + return resultT; } -list -multiplyLists(list> &&lists) { - typedef list StringList; - assert(lists.size()); - StringList result(*lists.begin()); - lists.pop_front(); +TypeAnnotation +collapseColumn(const std::list& symbols) { + TypeAnnotation resultT; + if(!symbols.size()) return resultT; - boost::format concat("%s, %s"); - for (StringList &list: lists) { - StringList::const_iterator end = result.end(); - for (StringList::iterator expr1I = result.begin(); expr1I != end; ++expr1I) { - if (list.size() == 0) continue; + Gringo::Symbol symbolAny = symbols.front(); - StringList::const_iterator expr2I = list.begin(); - for (int expr2No = 0, size = list.size() - 1; expr2No < size; ++expr2No, ++expr1I) - result.push_back(str(concat %(*expr1I) %(*expr2I))); + switch(symbolAny.type()) { + case Gringo::SymbolType::Num: + { + return TypeAnnotation(TypePrimitive::Int); + } - *expr1I = str(concat %(*expr1I) %(*expr2I)); - } + case Gringo::SymbolType::Str: + { + return TypeAnnotation(TypePrimitive::String); } - return result; + case Gringo::SymbolType::Fun: + { + map> fnGroups; + + for(const Gringo::Symbol& row : symbols) { + fnGroups[row.name().c_str()].push_back(row); + } + + TypeAnnotation resultT; + resultT.__operands.reserve(fnGroups.size()); + resultT.bindings.reserve(fnGroups.size()); + + for(const auto& group : fnGroups) { + if(!group.second.size()) continue; + + TypeAnnotation variantT = collapseFnGroup(group.second); + Gringo::Symbol symbolAny = group.second.front(); + string variantName = symbolAny.name().c_str(); + resultT.fields.push_back(variantName); + resultT.__operands.push_back(variantT); + } + + resultT.__operator = TypeOperator::VARIANT; + // if(resultT.__operands.size() == 1) { + // return resultT.__operands.front(); + // } + return resultT; + } + + case Gringo::SymbolType::Inf: + case Gringo::SymbolType::Special: + case Gringo::SymbolType::Sup: + { + break; + } + } + + assert(false); + return TypeAnnotation(); +} + +ExpandedType +dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend) { + assert(t->__operator == TypeOperator::SLAVE); + const string& domain = t->__valueCustom; + StaticModel model = transcend->query(domain); + if(!model.size()) return ExpandedType(TypeAnnotation()); + + std::list symbols; + for(auto row : model) { + symbols.push_back(row.second); + } + return ExpandedType(collapseFnGroup(symbols)); +} + +ASTSitePacked +getSite(const Expression& e, TranscendLayer* transcend){ + Attachments::put(e, e); + return transcend->pack(ASTSite{e.id}); +} + +Expression +recognizeSite(const Gringo::Symbol& atom, const ExpandedType& schemaT, TranscendLayer* transcend) +{ + if(atom.type()!=Gringo::SymbolType::Fun) return Expression(); + string predicateName = atom.name().c_str(); + if (predicateName != SITE_SYMBOL_PREDICATE && predicateName != SITE_ANON_PREDICATE) + return Expression(); + + const ASTSitePacked& siteP = TranscendLayer::parseAtom(atom); + const ASTSite& site = transcend->unpack(siteP); + return site.getDefinition(); } -std::list -compileNeg(const Expression &e){ - list result; - switch (e.op) { - case Operator::IMPL: { - assert(e.__state == Expression::COMPOUND); - assert(e.operands.size() == 2); - list operands1 = compile(e.operands.at(0)); - list operands2 = compile(e.operands.at(1)); - - boost::format formatNeg("%1%, not %2%"); - for (const auto &op1: operands1) - for (const auto &op2: operands2) { - result.push_back(boost::str(formatNeg %(op1) % (op2))); - } - break; +Expression +representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend) { + const Expression siteExpr = recognizeSite(atom, schemaT, transcend); + if (siteExpr.isValid()) return siteExpr; + + switch(schemaT->__operator) { + case TypeOperator::NONE: + { + switch(schemaT->__value) { + case TypePrimitive::I8: + case TypePrimitive::I32: + case TypePrimitive::I64: + case TypePrimitive::Int: + { + return Expression(Atom(atom.num())); } - case Operator::NEG: { - assert(e.operands.size() == 1); - const Expression &op = e.operands.at(0); - list &&rawOp = compile(op); + case TypePrimitive::String: + { + return Expression(Atom(atom.string().c_str())); + } - assert(rawOp.size() == 1); - result.push_back(rawOp.front()); - break; - }; + case TypePrimitive::Invalid: + case TypePrimitive::Bool: + case TypePrimitive::Float: + { + assert(false); + return Expression(); + } + } + break; + } - default: - assert(true); + case TypeOperator::SLAVE: + { + ExpandedType contentT = dereferenceSlaveType(schemaT, transcend); + return representTransExpression(atom, contentT, transcend); } - return result; -} + case TypeOperator::VARIANT: + { + map dictVariants; + for(size_t variantId = 0; variantId < schemaT->fields.size(); ++variantId) { + dictVariants.emplace(schemaT->fields.at(variantId), variantId); + } + + string predicateName = atom.name().c_str(); + assert(dictVariants.count(predicateName)); + + size_t predicateId = dictVariants.at(predicateName); + Expression result(Operator::VARIANT,{}); + result.op = Operator::VARIANT; + result.setValueDouble(predicateId); + + if(!schemaT->__operands.size()) return result; + ExpandedType schemeOperands = schemaT->__operands.at(predicateId).__operator == TypeOperator::SLAVE + ? dereferenceSlaveType(ExpandedType(schemaT->__operands.at(predicateId)), transcend) + : ExpandedType(schemaT->__operands.at(predicateId)); + + if (schemeOperands->__operator == TypeOperator::RECORD){ + Expression operandsList = representTransExpression(atom, schemeOperands, transcend); + result.operands = move(operandsList.operands); + + } else if(!schemeOperands->isValid()) { + assert(atom.args().size == 0); + return result; + + } else { + assert(false); + } -//NOTE: Any changes should be reflected in ParseImplAtom, -// ParseImplAtom -class VisitorFormatSymbol: public boost::static_visitor { -public: + return result; + } - boost::format operator()(const SymbolPacked& node) const { - boost::format formatSymbNamed("s(%1%,%2%,%3%)"); - return formatSymbNamed % node.identifier % node.version % node.scope ; + case TypeOperator::RECORD: + { + const Gringo::SymSpan& operandsRaw = atom.args(); + size_t opCount = operandsRaw.size; + assert(opCount == schemaT->__operands.size()); + + size_t operandId = 0; + std::vector operands; + operands.reserve(opCount); + for(const TypeAnnotation operandT : schemaT->__operands) { + operands.push_back(representTransExpression(operandsRaw[operandId], ExpandedType(operandT), transcend)); + ++operandId; + } + + Expression result(Operator::LIST,{}); + result.operands = operands; + result.type = schemaT; + return result; } - boost::format operator()(const SymbolAnonymous& node) const { - boost::format formatSymbAnonymous("a(%1%)"); - return formatSymbAnonymous % node.id; + case TypeOperator::ARRAY: + case TypeOperator::ALIAS: + case TypeOperator::ACCESS: + case TypeOperator::REF: + case TypeOperator::VOID: + case TypeOperator::GUARD: + { + assert(false); + return Expression(); } -}; + } + + assert(false); + return Expression(); +} + +Expression +representVecStr(const std::vector& srcList){ + Expression dstE(Operator::LIST, {}); + + for(const string op: srcList){ + dstE.operands.push_back(Atom(string(op))); + } + + return dstE; +} + +Expression +findAnnById(unsigned annId, const ExpandedType& annT, const std::list& annotations){ + assert(annT->fields.size() > annId); + const string& annExpectedS = annT->fields.at(annId); + + for(const Expression& e: annotations){ + if (e.type.fields.size() <= annId) continue; + const string& annTestS = e.type.fields.at(annId); + + if (annTestS == annExpectedS) return e; + } -boost::format writeSymbolNode(const SymbolNode& symbol){ - return boost::apply_visitor(VisitorFormatSymbol(), symbol); + return Expression(); } }} //end of xreate::analysis diff --git a/cpp/src/analysis/utils.h b/cpp/src/analysis/utils.h index 82c325b..c47a0ee 100644 --- a/cpp/src/analysis/utils.h +++ b/cpp/src/analysis/utils.h @@ -1,36 +1,59 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: aux.h * Author: pgess * * Created on June 26, 2016, 6:49 PM */ #ifndef AUX_H #define AUX_H #include "ast.h" #include "transcendlayer.h" #include #include namespace xreate { namespace analysis { /** * \brief Compiles Xreate expression into ASP format recognizable by an external ASP solver. * @param e expression * @return textual expression's representation in an ASP format. */ std::list compile(const Expression &e); std::list compileNeg(const Expression &e); std::list multiplyLists(std::list> &&lists); -boost::format writeSymbolNode(const SymbolNode& symbol); +ASTSitePacked getSite(const Expression& e, TranscendLayer* transcend); + +/** + * \brief Converts a Transcend's fact to a Xreate's expression that can be interpreted further. Supports `query` keyword. + * @param atom Transcend's fact + * @param schemaT Type of the resulting expression + * @param transcend Transcend's instance + * @return converted Transcend' fact in form of Xreate's expression + */ +Expression representTransExpression(const Gringo::Symbol& atom, ExpandedType schemaT, TranscendLayer* transcend); + +/** + * \brief Expands slave type. + * @param t Slave type + * @param transcend Instance of Transcend + * @return The expanded slave type + */ +ExpandedType dereferenceSlaveType(ExpandedType t, const TranscendLayer* transcend); + +TypeAnnotation collapseColumn(const std::list& symbols); +std::list generateAllInstancesInDomain2(const ExpandedType& domainT); + +Expression representVecStr(const std::vector& srcList); +Expression findAnnById(unsigned annId, const ExpandedType& annT, const std::list& annotations); }} //end of xreate::analysis #endif /* AUX_H */ diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp index 2c24707..fd5eaec 100644 --- a/cpp/src/ast.cpp +++ b/cpp/src/ast.cpp @@ -1,971 +1,961 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: ast.cpp */ #include "ast.h" -#include "ExternLayer.h" #include "analysis/typeinference.h" +#include "analysis/predefinedanns.h" + +#ifdef XREATE_ENABLE_EXTERN + #include "ExternLayer.h" +#endif #include #include //TODO BDecl. forbid multiple body declaration (ExprTyped) namespace std { std::size_t hash::operator()(xreate::ScopedSymbol const& s) const { return s.id ^ (s.version << 2); } bool equal_to::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const { return __x.id == __y.id && __x.version == __y.version; } size_t hash::operator()(xreate::Symbol const& s) const { return hash()(s.identifier) ^ ((long int) s.scope << 1); } bool equal_to::operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const { return __x == __y; }; } using namespace std; namespace xreate { Atom::Atom(const std::wstring& value) { __value = wstring_to_utf8(value); } Atom::Atom(std::string && name) : __value(name) { } const std::string& Atom::get() const { return __value; } Atom::Atom(wchar_t* value) { //DEBT reconsider number literal recognition __value = wcstol(value, 0, 10); } Atom::Atom(int value) : __value(value) { } double Atom::get()const { return __value; } Atom::Atom(const std::wstring& value) { assert(value.size() >= 2); __value = wstring_to_utf8(value.substr(1, value.size() - 2)); } Atom::Atom(std::string && name) : __value(name) {} const std::string& Atom::get() const { return __value; } /** \brief xreate::Expression static information*/ class ExpressionHints { public: static bool isStringValueValid(const Expression& e) { switch (e.__state) { case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: return true; case Expression::NUMBER: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::CALL: return true; default: return false; } } } return false; } static bool isDoubleValueValid(const Expression& e) { switch (e.__state) { case Expression::NUMBER: return true; case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::VARIANT: return true; default: return false; } } } return false; } }; -class TypesResolver { -private: - const AST* ast; - std::map scope; - std::map signatures; - - ExpandedType expandType(const TypeAnnotation &t, const std::vector &args = std::vector()) { - return TypesResolver(ast, scope, signatures)(t, args); - } - - std::vector - expandOperands(const std::vector& operands) { - std::vector pack; - - pack.reserve(operands.size()); - std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()), - [this](const TypeAnnotation & t) { - return expandType(t); - }); - - return pack; - } - +class TypeResolver { public: +TypeResolver(const AST* ast, + TypeResolver * parent, + std::set trace, + const std::map& scope = std::map()) + : __ast(ast), __scope(scope), __trace(trace), __parent(parent) {} - TypesResolver(const AST* root, const std::map& scopeOuter = std::map(), - std::map signaturesOuter = std::map()) - : ast(root), scope(scopeOuter), signatures(signaturesOuter) { +ExpandedType +operator()(const TypeAnnotation &t, const std::vector &args = std::vector()) { + //assert(args.size() == t.bindings.size()); // invalid number of arguments + for (size_t i = 0; i < args.size(); ++i) { + __scope[t.bindings.at(i)] = args.at(i); + } + + switch (t.__operator) { + case TypeOperator::ARRAY:{ + assert(t.__operands.size() == 1); + + Expanded elTy = this->operator()(t.__operands.at(0)); + return ExpandedType(TypeAnnotation(TypeOperator::ARRAY, {elTy})); } - ExpandedType - operator()(const TypeAnnotation &t, const std::vector &args = std::vector()) { - //assert(args.size() == t.bindings.size()); // invalid number of arguments - for (size_t i = 0; i < args.size(); ++i) { - scope[t.bindings.at(i)] = args.at(i); - } - - switch (t.__operator) { - case TypeOperator::LIST_ARRAY: - { - assert(t.__operands.size() == 1); - - Expanded elTy = expandType(t.__operands.at(0)); - return ExpandedType(TypeAnnotation(tag_array, elTy, 0)); - } - - case TypeOperator::LIST_RECORD: - { - std::vector&& packOperands = expandOperands(t.__operands); - auto typNew = TypeAnnotation(TypeOperator::LIST_RECORD, move(packOperands)); - typNew.fields = t.fields; - - return ExpandedType(move(typNew)); - }; - - case TypeOperator::VARIANT: - { - std::vector&& packOperands = expandOperands(t.__operands); - auto typNew = TypeAnnotation(TypeOperator::VARIANT, move(packOperands)); - typNew.fields = t.fields; - - return ExpandedType(move(typNew)); - }; - - case TypeOperator::CALL: - { - std::string alias = t.__valueCustom; - - //find in local scope: - TypeAnnotation ty; - if (scope.count(alias)) { - ty = scope.at(alias); - - } else if (ast->__indexTypeAliases.count(alias)) { - ty = ast->__indexTypeAliases.at(alias); - - } else { - assert(false && "Undefined or external type"); - } - - std::vector&& operands = expandOperands(t.__operands); - TypeAnnotation signature(TypeOperator::CALL, move(operands)); - signature.__valueCustom = alias; - - if (signatures.count(signature)) { - auto link = TypeAnnotation(TypeOperator::LINK,{}); - link.conjuctionId = signatures.at(signature); - - return ExpandedType(move(link)); - } - - int cid = signatures.size(); - signatures[signature] = cid; - TypeAnnotation tyResult = expandType(ty, operands); - tyResult.conjuctionId = cid; - - return ExpandedType(move(tyResult)); - }; - - case TypeOperator::CUSTOM: - { - if (signatures.count(t)) { - return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t})); - } - signatures.emplace(t, signatures.size()); - - std::string alias = t.__valueCustom; - - //find in local scope: - if (scope.count(alias)) { - return expandType(scope.at(alias)); - } - - //find in general scope: - if (ast->__indexTypeAliases.count(alias)) { - return expandType(ast->__indexTypeAliases.at(t.__valueCustom)); - } + case TypeOperator::RECORD: + { + std::vector&& packOperands = expandOperands(t.__operands); + auto typNew = TypeAnnotation(TypeOperator::RECORD, move(packOperands)); + typNew.fields = t.fields; - //if type is unknown keep it as is. - return ExpandedType(TypeAnnotation(t)); - }; + return ExpandedType(move(typNew)); + }; - case TypeOperator::ACCESS: - { - std::string alias = t.__valueCustom; - ExpandedType tyAlias = ExpandedType(TypeAnnotation()); + case TypeOperator::VARIANT: + { + std::vector&& packOperands = expandOperands(t.__operands); + auto typNew = TypeAnnotation(TypeOperator::VARIANT, move(packOperands)); + typNew.fields = t.fields; - //Find in local scope: - if (scope.count(alias)) { - tyAlias = expandType(scope.at(alias)); + return ExpandedType(move(typNew)); + }; - //Find in global scope: - } else if ((ast->__indexTypeAliases.count(alias))) { - tyAlias = expandType(ast->__indexTypeAliases.at(alias)); + case TypeOperator::ALIAS: { + std::string alias = t.__valueCustom; + if (__trace.count(alias)){ + assert(false && "Recursive Type"); + return ExpandedType(TypeAnnotation()); + } - } else { - assert(false && "Undefined or external type"); - } + const TypeAnnotation& tyAlias = findType(alias); + std::vector&& operands = expandOperands(t.__operands); + auto traceNew =__trace; + traceNew.insert(alias); + return TypeResolver(__ast, this, traceNew, __scope)(tyAlias, operands); + }; - assert(tyAlias->__operator == TypeOperator::LIST_RECORD); + case TypeOperator::ACCESS: + { + std::string alias = t.__valueCustom; + const TypeAnnotation& ty = findType(alias); + TypeAnnotation tyAggr = this->operator()(ty); - for (const string& field : t.fields) { - auto fieldIt = std::find(tyAlias->fields.begin(), tyAlias->fields.end(), field); - assert(fieldIt != tyAlias->fields.end() && "unknown field"); + for (const string& field : t.fields) { + auto fieldIt = std::find(tyAggr.fields.begin(), tyAggr.fields.end(), field); + assert(fieldIt != tyAggr.fields.end() && "unknown field"); - int fieldId = fieldIt - tyAlias->fields.begin(); - tyAlias = expandType(tyAlias->__operands.at(fieldId)); - } + int fieldId = fieldIt - tyAggr.fields.begin(); + tyAggr = tyAggr.__operands.at(fieldId); + } - return tyAlias; - } + return ExpandedType(tyAggr); + } - case TypeOperator::NONE: - { - return ExpandedType(TypeAnnotation(t)); - } + case TypeOperator::NONE: + case TypeOperator::VOID: + case TypeOperator::SLAVE: + case TypeOperator::REF: { + return ExpandedType(t); + } - case TypeOperator::SLAVE: - { - return ExpandedType(t); - } + default: + assert(false); + } - default: - assert(false); - } + assert(false); + return ExpandedType(TypeAnnotation()); +} - assert(false); - return ExpandedType(TypeAnnotation()); +private: + const AST* __ast; + std::map __scope; + std::set __trace; + TypeResolver* __parent; + + std::vector + expandOperands(const std::vector& operands) { + std::vector pack; + + pack.reserve(operands.size()); + std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()), + [this](const TypeAnnotation & t) { + return this->operator()(t); + }); + return pack; + } + + TypeAnnotation findType(const std::string& alias){ + if (__scope.count(alias)) { + return __scope.at(alias); + + } else if (__parent){ + return __parent->findType(alias); + + } else if (__ast->__registryTypes.count(alias)){ + return __ast->__registryTypes.at(alias); } + + assert(false && "Undefined or external type"); + return TypeAnnotation(); + } }; TypeAnnotation::TypeAnnotation() : __operator(TypeOperator::NONE), __value(TypePrimitive::Invalid) { } TypeAnnotation::TypeAnnotation(TypePrimitive typ) -: __value(typ) { -} +: __value(typ) +{} TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list operands) : __operator(op), __operands(operands) { } TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector&& operands) : __operator(op), __operands(operands) { } -TypeAnnotation::TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size) -: TypeAnnotation(TypeOperator::LIST_ARRAY,{typ}) { - __size = size; -} - bool TypeAnnotation::isValid() const { return !(__value == TypePrimitive::Invalid && __operator == TypeOperator::NONE); } bool TypeAnnotation::operator<(const TypeAnnotation& t) const { if (__operator != t.__operator) return __operator < t.__operator; if (__operator == TypeOperator::NONE) return __value < t.__value; - if (__operator == TypeOperator::CALL || __operator == TypeOperator::CUSTOM || __operator == TypeOperator::ACCESS) { + if (__operator == TypeOperator::ALIAS || __operator == TypeOperator::ACCESS) { if (__valueCustom != t.__valueCustom) return __valueCustom < t.__valueCustom; } return __operands < t.__operands; } /* TypeAnnotation (struct_tag, std::initializer_list) {} */ void TypeAnnotation::addBindings(std::vector>&& params) { bindings.reserve(bindings.size() + params.size()); std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()), [](const Atom& ident) { return ident.get(); }); } void TypeAnnotation::addFields(std::vector>&& listFields) { fields.reserve(fields.size() + listFields.size()); std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()), [](const Atom& ident) { return ident.get(); }); } unsigned int Expression::nextVacantId = 0; Expression::Expression(const Atom& number) : Expression() { __state = NUMBER; op = Operator::INVALID; __valueD = number.get(); } Expression::Expression(const Atom& a) : Expression() { __state = STRING; op = Operator::INVALID; __valueS = a.get(); } Expression::Expression(const Atom &ident) : Expression() { __state = IDENT; op = Operator::INVALID; __valueS = ident.get(); } Expression::Expression(const Operator &oprt, std::initializer_list params) : Expression() { __state = COMPOUND; op = oprt; if (op == Operator::CALL) { assert(params.size() > 0); Expression arg = *params.begin(); assert(arg.__state == Expression::IDENT); __valueS = std::move(arg.__valueS); operands.insert(operands.end(), params.begin() + 1, params.end()); return; } operands.insert(operands.end(), params.begin(), params.end()); } void Expression::setOp(Operator oprt) { op = oprt; switch (op) { case Operator::INVALID: __state = INVALID; break; default: __state = COMPOUND; break; } } void Expression::addArg(Expression &&arg) { operands.push_back(arg); } void Expression::addTags(const std::list tags) const { std::transform(tags.begin(), tags.end(), std::inserter(this->tags, this->tags.end()), [](const Expression & tag) { return make_pair(tag.getValueString(), tag); }); } void Expression::addBindings(std::initializer_list> params) { addBindings(params.begin(), params.end()); } void Expression::bindType(TypeAnnotation t) { type = move(t); } void Expression::addBlock(ManagedScpPtr scope) { blocks.push_back(scope.operator->()); } const std::vector& Expression::getOperands() const { return operands; } double Expression::getValueDouble() const { return __valueD; } const std::string& Expression::getValueString() const { return __valueS; } void Expression::setValue(const Atom&& v) { __valueS = v.get(); } void Expression::setValueDouble(double value) { __valueD = value; } bool Expression::isValid() const { return (__state != INVALID); } bool Expression::isDefined() const { return (__state != BINDING && __state != INVALID); } Expression::Expression() : __state(INVALID), op(Operator::INVALID), id(nextVacantId++) { } namespace details { namespace inconsistent { +std::map +AST::__registryIntrinsics = {}; + AST::AST() { - Attachments::init(); - Attachments::init(); - Attachments::init(); - Attachments::init(); + Attachments::init(); + Attachments::init(); + Attachments::init(); + Attachments::init(); + Attachments::init(); + + initIntrinsics(); + analysis::PredefinedAnns::instance().fillRegistry(__registryVariants); } void AST::addInterfaceData(const ASTInterface& interface, Expression&& data) { __interfacesData.emplace(interface, move(data)); } void AST::addDFAData(Expression &&data) { __dfadata.push_back(data); } void -AST::addExternData(ExternData &&data) { - __externdata.insert(__externdata.end(), data.entries.begin(), data.entries.end()); +AST::addExternData(ExternData &&entry) { + //__externdata.push_back(entry); } void AST::add(Function* f) { __functions.push_back(f); - __indexFunctions.emplace(f->getName(), __functions.size() - 1); + __dictFunctions.emplace(f->getName(), __functions.size() - 1); } void AST::add(MetaRuleAbstract *r) { __rules.push_back(r); } void AST::add(TypeAnnotation t, Atom alias) { if (t.__operator == TypeOperator::VARIANT) { for (int i = 0, size = t.fields.size(); i < size; ++i) { - __dictVariants.emplace(t.fields[i], make_pair(t, i)); + __registryVariants.emplace(t.fields[i], make_pair(t, i)); } } - __indexTypeAliases.emplace(alias.get(), move(t)); + __registryTypes.emplace(alias.get(), move(t)); } ManagedScpPtr AST::add(CodeScope* scope) { this->__scopes.push_back(scope); return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes); } std::string AST::getModuleName() { const std::string name = "moduleTest"; return name; } ManagedPtr AST::findFunction(const std::string& name) { - int count = __indexFunctions.count(name); + int count = __dictFunctions.count(name); if (!count) { return ManagedFnPtr::Invalid(); } assert(count == 1); - auto range = __indexFunctions.equal_range(name); + auto range = __dictFunctions.equal_range(name); return ManagedPtr(range.first->second, &this->__functions); } std::list AST::getAllFunctions() const { const size_t size = __functions.size(); std::list result; for (size_t i = 0; i < size; ++i) { result.push_back(ManagedFnPtr(i, &this->__functions)); } return result; } //TASK select default specializations std::list -AST::getFunctionSpecializations(const std::string& fnName) const { - auto functions = __indexFunctions.equal_range(fnName); +AST::getFnSpecializations(const std::string& fnName) const { + auto functions = __dictFunctions.equal_range(fnName); std::list result; std::transform(functions.first, functions.second, inserter(result, result.end()), [this](auto f) { return ManagedFnPtr(f.second, &this->__functions); }); return result; } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__functions); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__scopes); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__rules); } void +AST::recognizeIntrinsic(Expression& fn) const { + assert(fn.op == Operator::CALL_INTRINSIC); + if (!__registryIntrinsics.count(fn.getValueString())){ + assert(false); + } + + IntrinsicFn fnCode = __registryIntrinsics.at(fn.getValueString()); + fn.op = Operator::CALL_INTRINSIC; + fn.setValueDouble((int) fnCode); +} + +bool AST::recognizeVariantConstructor(Expression& function) { assert(function.op == Operator::CALL); - std::string variant = function.getValueString(); - if (!__dictVariants.count(variant)) { - return; + if (!__registryVariants.count(variant)) { + return false; } - auto record = __dictVariants.at(variant); + auto record = __registryVariants.at(variant); const TypeAnnotation& typ = record.first; function.op = Operator::VARIANT; function.setValueDouble(record.second); function.type = typ; + return true; } Atom AST::recognizeVariantConstructor(Atom ident) { std::string variant = ident.get(); - assert(__dictVariants.count(variant) && "Can't recognize variant constructor"); - auto record = __dictVariants.at(variant); + assert(__registryVariants.count(variant) && "Can't recognize variant constructor"); + auto record = __registryVariants.at(variant); return Atom(record.second); } void AST::postponeIdentifier(CodeScope* scope, const Expression& id) { - bucketUnrecognizedIdentifiers.emplace(scope, id); + __bucketUnrecognizedIdentifiers.emplace(scope, id); } void AST::recognizePostponedIdentifiers() { - for (const auto& identifier : bucketUnrecognizedIdentifiers) { + for (const auto& identifier : __bucketUnrecognizedIdentifiers) { if (!identifier.first->recognizeIdentifier(identifier.second)) { //exception: Ident not found - std::cout << "Unknown symbol: " << identifier.second.getValueString() << std::endl; - assert(false && "Symbol not found"); + std::cout << "Unknown identifier: " << identifier.second.getValueString() << std::endl; + assert(false && "Unknown identifier"); } } } xreate::AST* AST::finalize() { //all finalization steps: recognizePostponedIdentifiers(); return reinterpret_cast (this); } +void +AST::initIntrinsics(){ + if (__registryIntrinsics.size()) return; + + __registryIntrinsics = { + {"array_init", IntrinsicFn::ARR_INIT}, + {"rec_fields", IntrinsicFn::REC_FIELDS} + }; +} } } //namespace details::incomplete Expanded AST::findType(const std::string& name) { // find in general scope: - if (__indexTypeAliases.count(name)) - return expandType(__indexTypeAliases.at(name)); + if (__registryTypes.count(name)) + return expandType(__registryTypes.at(name)); //if type is unknown keep it as is. - TypeAnnotation t(TypeOperator::CUSTOM,{}); + TypeAnnotation t(TypeOperator::ALIAS, {}); t.__valueCustom = name; return ExpandedType(move(t)); } Expanded AST::expandType(const TypeAnnotation &t) const { - return TypesResolver(this)(t); + return TypeResolver(this, nullptr, {}, {})(t); } ExpandedType -AST::getType(const Expression& expression) { - return typeinference::getType(expression, *this); +AST::getType(const Expression& e, const TypeAnnotation& expectedT) { + return typeinference::getType(e, expectedT, *this); } Function::Function(const Atom& name) : __entry(new CodeScope(0)) { __name = name.get(); } void Function::addTag(Expression&& tag, const TagModifier mod) { string name = tag.getValueString(); __tags.emplace(move(name), move(tag)); } const std::map& Function::getTags() const { return __tags; } CodeScope* Function::getEntryScope() const { return __entry; } void Function::addBinding(Atom && name, Expression&& argument, const VNameId hintBindingId) { __entry->addBinding(move(name), move(argument), hintBindingId); } const std::string& Function::getName() const { return __name; } ScopedSymbol CodeScope::registerIdentifier(const Expression& identifier, const VNameId hintBindingId) { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); auto result = __identifiers.emplace(identifier.getValueString(), hintBindingId? hintBindingId: __identifiers.size() + 1); return { result.first->second, version }; } bool CodeScope::recognizeIdentifier(const Expression& identifier) const { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); const std::string& name = identifier.getValueString(); //search identifier in the current block if (__identifiers.count(name)) { VNameId id = __identifiers.at(name); Symbol s; s.identifier = ScopedSymbol{id, version}; s.scope = const_cast (this); Attachments::put(identifier, s); return true; } //search in the parent scope if (__parent) { return __parent->recognizeIdentifier(identifier); } return false; } ScopedSymbol CodeScope::getSymbol(const std::string& alias) { assert(__identifiers.count(alias)); VNameId id = __identifiers.at(alias); return {id, versions::VERSION_NONE }; } void CodeScope::addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId) { argument.__state = Expression::BINDING; __bindings.push_back(var.getValueString()); ScopedSymbol binding = registerIdentifier(var, hintBindingId); __declarations[binding] = move(argument); } Symbol CodeScope::addDefinition(Expression&& var, Expression&& body) { ScopedSymbol s = registerIdentifier(var); __declarations[s] = move(body); return Symbol{s, this}; } CodeScope::CodeScope(CodeScope* parent) : __parent(parent) { } CodeScope::~CodeScope() { } void CodeScope::setBody(const Expression &body) { assert(__declarations.count(ScopedSymbol::RetSymbol)==0 && "Attempt to reassign scope body"); __declarations[ScopedSymbol::RetSymbol] = body; } const Expression& CodeScope::getBody() const{ return __declarations.at(ScopedSymbol::RetSymbol); } const Expression& CodeScope::getDefinition(const Symbol& symbol, bool flagAllowUndefined){ const CodeScope* self = symbol.scope; return self->getDefinition(symbol.identifier, flagAllowUndefined); } const Expression& CodeScope::getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined) const{ static Expression expressionInvalid; if (!__declarations.count(symbol)){ if (flagAllowUndefined) return expressionInvalid; assert(false && "Symbol's declaration not found"); } return __declarations.at(symbol); } void RuleArguments::add(const Atom &arg, DomainAnnotation typ) { emplace_back(arg.get(), typ); } void RuleGuards::add(Expression&& e) { push_back(e); } MetaRuleAbstract:: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards) : __args(std::move(args)), __guards(std::move(guards)) { } MetaRuleAbstract::~MetaRuleAbstract() { } RuleWarning:: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message) : MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) { } RuleWarning::~RuleWarning() { } void RuleWarning::compile(TranscendLayer& layer) { //TODO restore addRuleWarning //layer.addRuleWarning(*this); } bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id < s2.id) || (s1.id == s2.id && s1.version < s2.version); } bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id == s2.id) && (s1.version == s2.version); } bool operator<(const Symbol& s1, const Symbol& s2) { return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier); } bool operator==(const Symbol& s1, const Symbol& s2) { return (s1.scope == s2.scope) && (s1.identifier == s2.identifier); } +bool operator< (const ASTSite& s1, const ASTSite& s2){ + return s1.id < s2.id; +} + bool operator<(const Expression&a, const Expression&b) { if (a.__state != b.__state) return a.__state < b.__state; assert(a.__state != Expression::INVALID); switch (a.__state) { case Expression::IDENT: case Expression::STRING: return a.getValueString() < b.getValueString(); case Expression::NUMBER: return a.getValueDouble() < b.getValueDouble(); case Expression::COMPOUND: { assert(a.blocks.size() == 0); assert(b.blocks.size() == 0); if (a.op != b.op) { return a.op < b.op; } bool flagAValid = ExpressionHints::isStringValueValid(a); bool flagBValid = ExpressionHints::isStringValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueString() != b.getValueString()) { return a.getValueString() < b.getValueString(); } } flagAValid = ExpressionHints::isDoubleValueValid(a); flagBValid = ExpressionHints::isDoubleValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueDouble() != b.getValueDouble()) { return a.getValueDouble() < b.getValueDouble(); } } if (a.operands.size() != b.operands.size()) { return (a.operands.size() < b.operands.size()); } for (size_t i = 0; i < a.operands.size(); ++i) { bool result = a.operands[i] < b.operands[i]; if (result) return true; } return false; } case Expression::BINDING: case Expression::INVALID: assert(false); } return false; } bool Expression::operator==(const Expression& other) const { if (this->__state != other.__state) return false; if (ExpressionHints::isStringValueValid(*this)) { if (this->__valueS != other.__valueS) return false; } if (ExpressionHints::isDoubleValueValid(*this)) { if (this->__valueD != other.__valueD) return false; } if (this->__state != Expression::COMPOUND) { return true; } if (this->op != other.op) { return false; } if (this->operands.size() != other.operands.size()) { return false; } for (size_t i = 0; ioperands.size(); ++i) { if (!(this->operands[i] == other.operands[i])) return false; } assert(!this->blocks.size()); assert(!other.blocks.size()); return true; } const ScopedSymbol ScopedSymbol::RetSymbol = ScopedSymbol{0, versions::VERSION_NONE}; +Expression +ASTSite::getDefinition() const{ + if (Attachments::exists(id)){ + const Symbol& siteS = Attachments::get(id); + return CodeScope::getDefinition(siteS, true); + } + + return Attachments::get(id); +} } //end of namespace xreate diff --git a/cpp/src/ast.h b/cpp/src/ast.h index 5fee1f6..391b46d 100644 --- a/cpp/src/ast.h +++ b/cpp/src/ast.h @@ -1,745 +1,750 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: ast.h */ /** * \file ast.h * \brief A syntax tree representation and related code * * \sa xreate::AST */ #ifndef AST_H #define AST_H #include "attachments.h" +#include "utils.h" -#include -#include #include #include +#include +#include +#include #include #include -#include -#include "utils.h" #include - -namespace llvm { - class Value; -} +#include namespace xreate { struct ScopedSymbol; struct Symbol; } namespace std { template<> struct hash { std::size_t operator()(xreate::ScopedSymbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const; }; template<> struct hash { size_t operator()(xreate::Symbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const; }; } namespace xreate { struct String_t { }; struct Identifier_t { }; struct Number_t { }; struct Type_t { }; template class Atom { }; //DEBT store line:col for all atoms/identifiers template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; template<> class Atom { public: Atom(wchar_t* value); Atom(int value); double get()const; private: double __value; }; template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; enum class TypePrimitive { - Invalid, Bool, I8, I32, I64, Num, Int, Float, String + Invalid, Bool, I8, I32, I64, Int, Float, String }; enum class TypeOperator { - NONE, CALL, CUSTOM, VARIANT, LIST_ARRAY, LIST_RECORD, ACCESS, LINK, SLAVE -}; - -struct llvm_array_tag { + NONE, VOID, REF, ALIAS, VARIANT, ARRAY, RECORD, ACCESS, SLAVE, GUARD }; struct struct_tag { }; -const llvm_array_tag tag_array = llvm_array_tag(); const struct_tag tag_struct = struct_tag(); /** * \brief A type representation to support type system * * The class represents type in a denormalized form, i.e. with no arguments and aliases substitution * \sa AST::expandType() */ class TypeAnnotation { public: TypeAnnotation(); - TypeAnnotation(const Atom& typ); TypeAnnotation(TypePrimitive typ); - TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size); TypeAnnotation(TypeOperator op, std::initializer_list operands); TypeAnnotation(TypeOperator op, std::vector&& operands); void addBindings(std::vector>&& params); void addFields(std::vector>&& listFields); bool operator<(const TypeAnnotation& t) const; // TypeAnnotation (struct_tag, std::initializer_list); bool isValid() const; TypeOperator __operator = TypeOperator::NONE; std::vector __operands; TypePrimitive __value; std::string __valueCustom; - int conjuctionId = -1; //conjunction point id (relevant for recursive types) - uint64_t __size = 0; std::vector fields; std::vector bindings; private: }; enum class Operator { - INVALID, UNDEF, ADD, SUB, MUL, DIV, + INVALID, UNDEF, AND, OR, ADD, SUB, MUL, DIV, EQU, NE, NEG, LSS, LSE, GTR, GTE, LIST, - LIST_RANGE, + LIST_INDEX, LIST_RANGE, CALL, CALL_INTRINSIC, QUERY, QUERY_LATE, IMPL/* implication */, MAP, - FOLD, INF, INDEX, + FOLD, FOLD_INF, INDEX, IF, SWITCH, SWITCH_VARIANT, SWITCH_LATE, CASE, CASE_DEFAULT, LOGIC_AND, - CONTEXT_RULE, VARIANT, SEQUENCE + CONTEXT_RULE, VARIANT, SEQUENCE, UPDATE }; class Function; class AST; class CodeScope; class MetaRuleAbstract; typedef ManagedPtr ManagedFnPtr; typedef ManagedPtr ManagedScpPtr; typedef ManagedPtr ManagedRulePtr; const ManagedScpPtr NO_SCOPE = ManagedScpPtr(UINT_MAX, 0); /** * \brief AST node to represent a single instruction or an annotation * \attention In case of any changes update \ref xreate::ExpressionHints auxiliary helper as well * * %Expression is a generic building block of syntax tree which is able to hold node data * along with child nodes as operands. * * \note For types the %expression-like data structure \ref TypeAnnotation is used rather than Expression itself. * \sa xreate::AST, xreate::TypeAnnotation */ struct Expression { friend class CodeScope; friend class TranscendLayer; friend class CFAPass; friend class ExpressionHints; Expression(const Operator &oprt, std::initializer_list params); Expression(const Atom& ident); Expression(const Atom& number); Expression(const Atom& a); Expression(); void setOp(Operator oprt); void addArg(Expression&& arg); void addBindings(std::initializer_list> params); void bindType(TypeAnnotation t); template void addBindings(InputIt paramsBegin, InputIt paramsEnd); void addTags(const std::list tags) const; void addBlock(ManagedScpPtr scope); const std::vector& getOperands() const; double getValueDouble() const; void setValueDouble(double value); const std::string& getValueString() const; void setValue(const Atom&& v); bool isValid() const; bool isDefined() const; bool operator==(const Expression& other) const; /** * \brief is it string, number, compound operation and so on */ enum { INVALID, COMPOUND, IDENT, NUMBER, STRING, BINDING } __state = INVALID; /** * \brief Valid for compound State. Holds type of compound operator */ Operator op; /** * \brief Unique id to identify expression within syntax tree */ unsigned int id; /** * \brief Exact meaning depends on particular instruction * \details As an example, named lists/structs hold field names in bindings */ std::vector bindings; - std::map __indexBindings; - + /** * \brief Holds child instructions as arguments */ std::vector operands; /** * \brief Holds type of instruction's result */ TypeAnnotation type; /** * \brief Holds additional annotations */ mutable std::map tags; /** * \brief Child code blocks * \details For example, If statement holds TRUE-branch as first and FALSE-branch as second block here */ std::list blocks; private: std::string __valueS; double __valueD; static unsigned int nextVacantId; }; bool operator<(const Expression&, const Expression&); template void Expression::addBindings(InputIt paramsBegin, InputIt paramsEnd) { size_t index = bindings.size(); std::transform(paramsBegin, paramsEnd, std::inserter(bindings, bindings.end()), [&index, this] (const Atom atom) { std::string key = atom.get(); - this->__indexBindings[key] = index++; return key; }); } typedef std::list ExpressionList; enum class TagModifier { NONE, ASSERT, REQUIRE }; enum class DomainAnnotation { FUNCTION, VARIABLE }; class RuleArguments : public std::vector> { public: void add(const Atom& name, DomainAnnotation typ); }; class RuleGuards : public std::vector { public: void add(Expression&& e); }; class TranscendLayer; class LLVMLayer; class MetaRuleAbstract { public: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards); virtual ~MetaRuleAbstract(); virtual void compile(TranscendLayer& layer) = 0; protected: RuleArguments __args; RuleGuards __guards; }; class RuleWarning : public MetaRuleAbstract { friend class TranscendLayer; public: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message); virtual void compile(TranscendLayer& layer); ~RuleWarning(); private: std::string __message; Expression __condition; }; typedef unsigned int VNameId; namespace versions { typedef int VariableVersion; const VariableVersion VERSION_NONE = -2; const VariableVersion VERSION_INIT = 0; } template<> struct AttachmentsDict { typedef versions::VariableVersion Data; static const unsigned int key = 6; }; struct ScopedSymbol { VNameId id; versions::VariableVersion version; static const ScopedSymbol RetSymbol; }; struct Symbol { ScopedSymbol identifier; const CodeScope * scope; }; +struct ASTSite { + unsigned int id; + + Expression getDefinition() const; + //static Ast registerSite(const Expression& e); +}; + struct IdentifierSymbol{}; -struct SymbolAlias{}; +struct ExprAlias_A{}; +struct ExprId_A{}; template<> struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 7; }; template<> -struct AttachmentsDict { +struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 9; }; +template<> +struct AttachmentsDict{ + typedef Expression Data; + static const unsigned int key = 12; +}; + typedef std::pair Tag; bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator<(const Symbol& s1, const Symbol& s2); bool operator==(const Symbol& s1, const Symbol& s2); +bool operator< (const ASTSite& s1, const ASTSite& s2); /** * \brief AST node to represent a single code block/a scope of visibility * * Holds a single expression as a `body` along with set of variable assignments(declarations) used in body's expression. * \sa xreate::AST */ class CodeScope { friend class Function; friend class PassManager; public: CodeScope(CodeScope* parent = 0); ~CodeScope(); /** \brief Set expression as a body */ void setBody(const Expression& body); /** \brief Returns current code scope body */ const Expression& getBody() const; /** \brief Adds variable definition to be used in body as well as in other declarations */ Symbol addDefinition(Expression&& var, Expression&& body); /** \brief Returns symbols' definition */ static const Expression& getDefinition(const Symbol& symbol, bool flagAllowUndefined = false); const Expression& getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined = false) const; /** \brief Adds variable defined elsewhere */ void addBinding(Expression&& var, Expression&& argument, const VNameId hintBindingId = 0); std::vector __bindings; std::map __identifiers; CodeScope* __parent; //TODO move __definitions to SymbolsAttachments data //NOTE: definition of return type has index 0 std::unordered_map __declarations; std::vector tags; std::vector contextRules; private: ScopedSymbol registerIdentifier(const Expression& identifier, const VNameId hintBindingId = 0); public: bool recognizeIdentifier(const Expression& identifier) const; ScopedSymbol getSymbol(const std::string& alias); }; /** * \brief AST node to represent a single function * * Holds an `__entry` entry code scope along with `guard` to denote the different specializations. * \sa xreate::AST */ class Function { friend class Expression; friend class CodeScope; friend class AST; public: Function(const Atom& name); /** * \brief Adds function arguments */ void addBinding(Atom && name, Expression&& argument, const VNameId hintBindingId=0); /** * \brief Adds additional function annotations */ void addTag(Expression&& tag, const TagModifier mod); const std::string& getName() const; const std::map& getTags() const; CodeScope* getEntryScope() const; CodeScope* __entry; std::string __name; - bool isPrefunction = false; //SECTIONTAG adhoc Function::isPrefunction flag - Expression guard; - + private: std::map __tags; }; class ExternData; -struct ExternEntry { - std::string package; - std::vector headers; -}; - typedef Expanded ExpandedType; struct TypeInferred{}; template<> struct AttachmentsDict { typedef ExpandedType Data; static const unsigned int key = 11; }; enum ASTInterface { CFA, DFA, Extern, Adhoc }; struct FunctionSpecialization { std::string guard; size_t id; }; struct FunctionSpecializationQuery { std::unordered_set context; }; template<> struct AttachmentsId{ static unsigned int getId(const Expression& expression){ return expression.id; } }; template<> struct AttachmentsId{ static unsigned int getId(const Symbol& s){ return s.scope->__declarations.at(s.identifier).id; } }; template<> struct AttachmentsId{ static unsigned int getId(const ManagedFnPtr& f){ const Symbol symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; return AttachmentsId::getId(symbolFunction); } }; template<> struct AttachmentsId{ static unsigned int getId(const CodeScope* scope){ const Symbol symbolScope{ScopedSymbol::RetSymbol, scope}; return AttachmentsId::getId(symbolScope); } }; template<> struct AttachmentsId{ static unsigned int getId(const unsigned int id){ return id; } }; -class TypesResolver; +class TypeResolver; + +enum class IntrinsicFn { + ARR_INIT, + REC_FIELDS +}; + namespace details { namespace inconsistent { /** * \brief AST in an inconsistent form during construction * * Represents AST under construction(**inconsistent state**). * \attention Clients should use rather xreate::AST unless client's code explicitly works with Syntax Tree during construction. * * Typically an instance is created by xreate::XreateManager only and filled out by the parser * \sa xreate::XreateManager::prepare(std::string&&) */ class AST { - friend class xreate::TypesResolver; + friend class xreate::TypeResolver; public: AST(); /** * \brief Adds new function to AST * \param f Function to register */ void add(Function* f); /** * \brief Adds new declarative rule to AST * \param r Declarative Rule */ void add(MetaRuleAbstract* r); /** \brief Registers new code block */ ManagedScpPtr add(CodeScope* scope); /** * \brief Add new type to AST * @param t Type definition * @param alias Typer name */ void add(TypeAnnotation t, Atom alias); /** \brief Current module's name */ std::string getModuleName(); /** * \brief Looks for function with given name * \param name Function name to find * \note Requires that only one function exists under given name * \return Found function */ ManagedPtr findFunction(const std::string& name); /** \brief Returns all function in AST */ std::list getAllFunctions() const; /** * \brief Returns all specializations of a function with a given name * \param fnName function to find * \return list of found function specializations */ - std::list getFunctionSpecializations(const std::string& fnName) const; + std::list getFnSpecializations(const std::string& fnName) const; /** * \return First element in Functions/Scopes/Rules list depending on template parameter * \tparam Target either Function or CodeScope or MetaRuleAbstract */ template ManagedPtr begin(); /** * \brief Performs all necessary steps after AST is built * * Performs all finalization steps and moves AST into consistent state represented by xreate::AST * \sa xreate::AST * \return AST in consistent state */ xreate::AST* finalize(); typedef std::multimap FUNCTIONS_REGISTRY; - - std::vector __externdata; + + //std::list __externdata; std::list __dfadata; //TODO move to more appropriate place std::list __rawImports; //TODO move to more appropriate place std::multimap __interfacesData; //TODO CFA data here. private: std::vector __rules; std::vector __functions; std::vector __scopes; - FUNCTIONS_REGISTRY __indexFunctions; + FUNCTIONS_REGISTRY __dictFunctions; protected: - std::map __indexTypeAliases; + std::map __registryTypes; public: /** * \brief Stores DFA scheme for later use by DFA Pass * * Treats expression as a DFA scheme and feeds to the DFA Pass later * \param data DFA Scheme * \sa xreate::DFAPass */ void addDFAData(Expression&& data); /** \brief Stores data for later use by xreate::ExternLayer */ - void addExternData(ExternData&& data); + void addExternData(ExternData&& entry); /** * \brief Generalized function to store particular data for later use by particular pass * \param interface Particular Interface * \param data Particular data */ void addInterfaceData(const ASTInterface& interface, Expression&& data); /**\name Symbols Recognition */ ///@{ public: //TODO revisit enums/variants, move to codescope /** * \brief Tries to find out whether expression is Variant constructor */ - void recognizeVariantConstructor(Expression& function); + bool recognizeVariantConstructor(Expression& function); Atom recognizeVariantConstructor(Atom ident); + /** + * \brief Postpones unrecognized identifier for future second round of recognition + * \param scope Code block identifier is encountered + * \param id Identifier + */ + void postponeIdentifier(CodeScope* scope, const Expression& id); + /** \brief Second round of identifiers recognition done right after AST is fully constructed */ + void recognizePostponedIdentifiers(); + + void recognizeIntrinsic(Expression& fn) const; private: - std::map> __dictVariants; + std::map> __registryVariants; + static std::map __registryIntrinsics; + std::set> __bucketUnrecognizedIdentifiers; -public: - std::set> bucketUnrecognizedIdentifiers; + static void initIntrinsics(); public: - /** - * \brief Postpones unrecognized identifier for future second round of recognition - * \param scope Code block identifier is encountered - * \param id Identifier - */ - void postponeIdentifier(CodeScope* scope, const Expression& id); - - /** \brief Second round of identifiers recognition done right after AST is fully constructed */ - void recognizePostponedIdentifiers(); + ///@} }; template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); } } // namespace details::incomplete /** * \brief AST in a consistent state * * AST has two mutually exclusive possible states: * - an inconsistent state while AST is under construction. Represented by xreate::details::inconsistent::AST * - a consistent state when AST is built and finalize() is invoked. * * This class represents a consistent state and should be used by clients unless client's code explicitly works with AST under construction. * Consistent AST enables access to additional functions(such as type management). * \sa xreate::details::inconsistent::AST */ class AST : public details::inconsistent::AST { public: AST() : details::inconsistent::AST() {} /** * \brief Computes fully expanded form of type by substituting all arguments and aliases * \param t Type to expand * \return Expdanded or normal form of type * \sa TypeAnnotation */ ExpandedType expandType(const TypeAnnotation &t) const; /** * Searches type by given name * \param name Typename to search * \return Expanded or normal form of desired type * \note if type name is not found returns new undefined type with this name */ ExpandedType findType(const std::string& name); /** * Invokes Type Inference Analysis to find out expanded(normal) form expressions's type * \sa typeinference.h - * \param expression + * \param e + * \param expectedT expected type * \return Type of expression */ - ExpandedType getType(const Expression& expression); + ExpandedType getType(const Expression& e, const TypeAnnotation& expectedT = TypeAnnotation()); }; } #endif // AST_H diff --git a/cpp/src/attachments.h b/cpp/src/attachments.h index 8ef3663..8b9c0cb 100644 --- a/cpp/src/attachments.h +++ b/cpp/src/attachments.h @@ -1,183 +1,184 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: attachments.h * Date: 3/15/15 */ /** * \file attachments.h * \brief The attachments support: a mechanism to attach additional data to AST nodes */ #ifndef _XREATE_ATTACHMENTS_H_ #define _XREATE_ATTACHMENTS_H_ #include #include #include #include namespace xreate { /** \brief Attachments dictionary */ template struct AttachmentsDict { // typedef void Data; // static const unsigned int key (next vacant id - 12); // Defined attachments: //----------------------------------------------------- // 1 containers::Implementation // 3 interpretation::InterpretationData -// 5 interpretation::FunctionInterpretationData +// 5 interpretation::I12nFunctionSpec // 6 VariableVersion // 7 IdentifierSymbol // 8 versions::VersionImposedDependency -// 9 SymbolAlias +// 9 ExprAlias_A // 11 TypeInferred +// 12 ExprId_A }; template struct AttachmentsId{ //static unsigned int getId(const Object& object); }; template class IAttachmentsContainer{ protected: virtual bool __exists(const unsigned int object)=0; virtual Data& __get(const unsigned int object)=0; virtual void __put(const unsigned int object, Data data)=0; public: template bool exists(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __exists(id); } template Data& get(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __get(id); } template Data get(const Id& object, const Data& dataDefault){ unsigned int id = AttachmentsId::getId(object); if (! __exists(id)){ return dataDefault; } return __get(id); } template void put(const Id& object, Data data){ unsigned int id = AttachmentsId::getId(object); __put(id, data); } virtual ~IAttachmentsContainer(){}; }; template class AttachmentsContainerDefault: public IAttachmentsContainer{ private: std::unordered_map __data; virtual bool __exists(const unsigned int id){ return __data.count(id); } virtual Data& __get(const unsigned int id){ return __data.at(id); } virtual void __put(const unsigned int id, Data data){ auto result = __data.emplace(id, data); assert(result.second); } public: std::unordered_map& getRawStorage() { return __data; } }; /** \brief Implements %AST attachments mechanism to facilitate data sharing among different analyzers and compilation phases */ class Attachments{ private: static std::vector __storage; template using Data = typename AttachmentsDict::Data; public: template static bool exists(const Id& object) { assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->exists(object); } template static Data& get(const Id& object){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object); } template static Data get(const Id& object, const Data& dataDefault){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object, dataDefault); } template static void put(const Id& object, Data data){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); self->put(object, data); } template static void init(){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = new AttachmentsContainerDefault>(); } template static void init(IAttachmentsContainer>* container){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = container; } }; } #endif //_XREATE_ATTACHMENTS_H_ diff --git a/cpp/src/aux/expressions.cpp b/cpp/src/aux/expressions.cpp new file mode 100644 index 0000000..67428a6 --- /dev/null +++ b/cpp/src/aux/expressions.cpp @@ -0,0 +1,60 @@ +#include "expressions.h" + +namespace xreate{ + +Expression +getVariantData(const Expression &variantE, ExpandedType variantT){ + Expression result(Operator::LIST, {}); + TypeAnnotation variantDataT(variantT->__operands.at(variantE.getValueDouble())); + if(!variantDataT.isValid()) return Expression(); + + assert(variantDataT.__operator == TypeOperator::RECORD); + result.operands = variantE.operands; + result.bindings = variantDataT.fields; + result.type = variantDataT; + return result; +} + +ListDictionary +reprListAsDict(const Expression &e){ + ListDictionary result; + assert(e.__state == Expression::COMPOUND); + + switch(e.op){ + case Operator::LIST:{ + Expression keyE; + int lastPos = 0; + for(size_t i = 0; i < e.operands.size(); ++i){ + const Expression &valueE = e.operands.at(i); + const std::string &keyS = e.bindings.at(i); + + if(keyS.empty()){ + keyE = Expression(Atom(lastPos++)); + } else{ + keyE = Expression(Atom(std::string(keyS))); + } + + result.emplace(keyE, valueE); + } + break; + } + + case Operator::LIST_INDEX:{ + auto opI = e.operands.cbegin(); + while(opI != e.operands.cend()){ + const Expression &keysListE = *(opI++); + const Expression &valueE = *(opI++); + assert(keysListE.operands.size() == 1); + + result.emplace(keysListE.operands.at(0), valueE); + } + break; + } + default: + assert(false); + } + + return result; +} + +} \ No newline at end of file diff --git a/cpp/src/aux/expressions.h b/cpp/src/aux/expressions.h new file mode 100644 index 0000000..4be22b8 --- /dev/null +++ b/cpp/src/aux/expressions.h @@ -0,0 +1,15 @@ +// +// Created by pgess on 31/01/2020. +// + +#ifndef XREATE_EXPRESSIONS_H +#define XREATE_EXPRESSIONS_H + +#include "ast.h" + +namespace xreate{ + typedef std::map ListDictionary; + Expression getVariantData(const Expression& variantE, ExpandedType variantT); + ListDictionary reprListAsDict(const Expression& e); +} +#endif //XREATE_EXPRESSIONS_H diff --git a/cpp/src/aux/serialization/expressionserializer.cpp b/cpp/src/aux/serialization/expressionserializer.cpp index e450529..4917f94 100644 --- a/cpp/src/aux/serialization/expressionserializer.cpp +++ b/cpp/src/aux/serialization/expressionserializer.cpp @@ -1,321 +1,321 @@ /* 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/. * * expressionserializer.cpp * * Created on: Jan 4, 2016 * Author: pgess */ #include "aux/serialization/expressionserializer.h" #include #include - #include +#include using namespace std; //using namespace boost::bimaps; namespace xreate { struct Index { string name; size_t degree; //count of parameters unsigned char level; //level in expression tree (depth of tree layer) bool operator< (const Index other) const{ if (name != other.name) return name < other.name; if (degree != other.degree) return degree < other.degree; if (name != other.name) return level < other.level; return false; } }; class ExpressionSerializerPrivate { //boost::bimap> __registry; struct { map left; } __registry; map __range; public: void pack(const Expression& e, unsigned char level, OptionalPackedExpression& target){ if (!target) return; switch (e.op){ case Operator::INVALID: { switch (e.__state) { case Expression::NUMBER: case Expression::STRING: case Expression::IDENT : { Index index; if ((e.__state == Expression::NUMBER)) index = {std::to_string(e.getValueDouble()), 0, level}; else index = {e.getValueString(), 0, level}; if (!__registry.left.count(index)){ target = boost::none; return; } size_t id = __registry.left.at(index); size_t range = __range[level]; (*target) << make_pair(id, range); return; } default: break; } break; } case Operator::CALL: { Index index{e.getValueString(), e.operands.size(), level}; if(!__registry.left.count(index)){ target = boost::none; return; } size_t id = __registry.left.at(index); size_t range = __range[level]; (*target) << make_pair(id, range); for (const Expression& operand: e.operands){ pack(operand, level+1, target); } return; } default: break; } assert(false && "Expression too complicate for serialization"); } void registerExpression(const Expression&e, unsigned char level){ switch (e.op){ case Operator::CALL: { Index index{e.getValueString(), e.operands.size(), level}; if (__registry.left.insert(make_pair(index, __range[level])).second){ __range[level]++; } for (const Expression& operand: e.operands){ registerExpression(operand, level+1); } return; } case Operator::INVALID: { Index index; switch (e.__state) { case Expression::STRING: case Expression::IDENT: { index = {e.getValueString(), 0, level}; if (__registry.left.insert(make_pair(index, __range[level])).second){ __range[level]++; } return; } case Expression::NUMBER: { index = {std::to_string(e.getValueDouble()), 0, level}; if (__registry.left.insert(make_pair(index, __range[level])).second){ __range[level]++; } return; } default: break; } break; } default: break; } assert(false && "Expression too complicate for serialization"); } }; ExpressionSerializer::ExpressionSerializer() : strategy(new ExpressionSerializerPrivate()){ } ExpressionSerializer::~ExpressionSerializer() { delete strategy; } void ExpressionSerializer::registerExpression(const Expression&e){ if (e.isValid()) strategy->registerExpression(e, 0); } PackedExpression ExpressionSerializer::getId(const Expression& e){ OptionalPackedExpression result(boost::in_place()); //move(PackedExpression()) strategy->pack(e, 0, result); assert(result); return move(*result); } OptionalPackedExpression ExpressionSerializer::getIdOptional(const Expression& e) const{ OptionalPackedExpression result(boost::in_place()); //move(PackedExpression()) strategy->pack(e, 0, result); return result; } ExpressionSerializerIntegral::ExpressionSerializerIntegral():serializer(*this){} ExpressionSerializerIntegral::ExpressionSerializerIntegral(const std::vector&& expressions) : std::vector(move(expressions)), serializer(*this){ size_t id =0; for (const Expression& e: expressions){ __registry.emplace(serializer.getId(e), id++); } } size_t ExpressionSerializerIntegral::size() const{ return PARENT::size(); } size_t ExpressionSerializerIntegral::count(const Expression& e) const { return (getIdOptional(e)? 1: 0); } ExpressionSerializerIntegral::const_iterator ExpressionSerializerIntegral::begin() const { return PARENT::begin(); } ExpressionSerializerIntegral::const_iterator ExpressionSerializerIntegral::end() const { return PARENT::end(); } size_t ExpressionSerializerIntegral::getId(const Expression& e) const{ const OptionalPackedExpression exprPacked = serializer.getIdOptional(e); assert(exprPacked); return __registry.at(*exprPacked); } boost::optional ExpressionSerializerIntegral::getIdOptional(const Expression& e) const{ const OptionalPackedExpression exprPacked = serializer.getIdOptional(e); if (!exprPacked){ return boost::none; } return __registry.at(*exprPacked); } const Expression& ExpressionSerializerIntegral::get(size_t id) const{ return at(id); } void PackedExpression::operator<< (const std::pair& value){ static const size_t sizeSizeT = sizeof(size_t); const size_t& id = value.first; const size_t& range = value.second; int countSufficientBits = range <=1? 0 : ceil(log2(range)); if (0 < countRemainedBits && countRemainedBits < countSufficientBits) { size_t* tail = reinterpret_cast(__storage + size- sizeSizeT); (*tail) += id >> (countSufficientBits - countRemainedBits); countSufficientBits-=countRemainedBits; countRemainedBits = 0; } if (countRemainedBits == 0) { if (countSufficientBits == 0) return; char* __storageNew = new char[size+sizeSizeT]; std::memcpy (__storageNew, __storage, size); std::memset(__storageNew + size, 0, sizeSizeT); delete[] __storage; __storage = __storageNew; size += sizeSizeT; countRemainedBits = 8 * sizeSizeT; } if (countRemainedBits >= countSufficientBits) { size_t* tail = reinterpret_cast(__storage + size- sizeSizeT); (*tail) += id << (countRemainedBits - countSufficientBits); countRemainedBits -= countSufficientBits; return; } assert("Unreachable block"); } #if BOOST_VERSION <= 105500 PackedExpression::PackedExpression(const PackedExpression& other){ __storage = other.__storage; size = other.size; countRemainedBits = other.countRemainedBits; } #endif PackedExpression::PackedExpression(PackedExpression&& other){ __storage = other.__storage; size = other.size; countRemainedBits = other.countRemainedBits; other.__storage = nullptr; } bool PackedExpression::operator==(const PackedExpression& other) const{ if (size == other.size && countRemainedBits == other.countRemainedBits){ return std::memcmp(__storage, other.__storage, size) == 0 ; } return false; } bool PackedExpression::operator<(const PackedExpression& other) const{ if (size < other.size) { return true; } if (countRemainedBits < other.countRemainedBits) return true; if (size == other.size && countRemainedBits == other.countRemainedBits){ return std::memcmp(__storage, other.__storage, size) < 0 ; } return false; } bool PackedExpression::operator!=(const PackedExpression& other) const{ return ! ((*this) == other); } PackedExpression::~PackedExpression() { delete[] __storage; } //PackedExpression::PackedExpression (const PackedExpression& other) // : size(other.size), countRemainedBits(other.countRemainedBits) //{ // __storage = new char[size]; // std::memcpy (__storage, other.__storage, size); //} } /* namespace xreate */ diff --git a/cpp/src/aux/xreatemanager-decorators.cpp b/cpp/src/aux/xreatemanager-decorators.cpp index 37ec862..caf685b 100644 --- a/cpp/src/aux/xreatemanager-decorators.cpp +++ b/cpp/src/aux/xreatemanager-decorators.cpp @@ -1,77 +1,86 @@ /* 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 "transcendlayer.h" +#include "analysis/transcendtarget.h" #include "pass/interpretationpass.h" -#include "pass/versionspass.h" -#include "pass/cfatemporalseqpass.h" + +#ifndef XREATE_CONFIG_MIN + #include "pass/cfapass.h" + #include "pass/dfapass.h" + #include "pass/versionspass.h" + #include "pass/cfatemporalseqpass.h" +#endif + 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() { + #ifndef XREATE_CONFIG_MIN 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); + #endif + + registerPass(new interpretation::InterpretationPass(this), PassId::InterpretationPass); + registerPass(new TranscendPass(this), PassId::TranscendPass); } 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/compilation/advancedinstructions.cpp b/cpp/src/compilation/advancedinstructions.cpp deleted file mode 100644 index b6ca440..0000000 --- a/cpp/src/compilation/advancedinstructions.cpp +++ /dev/null @@ -1,465 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * File: InstructionsAdvanced.cpp - * Author: pgess - * - * Created on June 26, 2016, 6:00 PM - */ - -#include "analysis/typeinference.h" -#include "compilation/advancedinstructions.h" -#include "compilation/containers.h" -#include "compilation/transformersaturation.h" - -#include "query/containers.h" -#include "llvmlayer.h" -#include "ast.h" - -using namespace std; -using namespace llvm; -using namespace xreate; -using namespace xreate::containers; -using namespace xreate::compilation; - -#define NAME(x) (hintRetVar.empty()? x : hintRetVar) -#define UNUSED(x) (void)(x) -#define EXPAND_CONTEXT \ - LLVMLayer* llvm = context.pass->man->llvm; \ - compilation::ICodeScopeUnit* scope = context.scope; \ - compilation::IFunctionUnit* function = context.function; - -AdvancedInstructions::AdvancedInstructions(compilation::Context ctx) -: context(ctx), tyNum(static_cast (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Num))))) { -} - -llvm::Value* -AdvancedInstructions::compileMapSolidOutput(const Expression &expr, const std::string hintRetVar) { - EXPAND_CONTEXT UNUSED(scope); - - //initialization - Symbol symbolIn = Attachments::get(expr.getOperands()[0]); - - ImplementationRec implIn = containers::Query::queryImplementation(symbolIn).extract(); // impl of input list - size_t size = implIn.size; - CodeScope* scopeLoop = expr.blocks.front(); - std::string varEl = scopeLoop->__bindings[0]; - - Iterator* it = Iterator::create(context, symbolIn); - llvm::Value *rangeFrom = it->begin(); - llvm::Value *rangeTo = it->end(); - - //definitions - ArrayType* tyNumArray = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::Num, size)))); - llvm::IRBuilder<> &builder = llvm->builder; - - llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "loop", function->raw); - llvm::BasicBlock *blockBeforeLoop = builder.GetInsertBlock(); - llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "postloop", function->raw); - Value* dataOut = llvm->builder.CreateAlloca(tyNumArray, ConstantInt::get(tyNum, size), NAME("map")); - - // * initial check - Value* condBefore = builder.CreateICmpSLE(rangeFrom, rangeTo); - builder.CreateCondBr(condBefore, blockLoop, blockAfterLoop); - - // create PHI: - builder.SetInsertPoint(blockLoop); - llvm::PHINode *stateLoop = builder.CreatePHI(tyNum, 2, "mapIt"); - stateLoop->addIncoming(rangeFrom, blockBeforeLoop); - - // loop body: - Value* elIn = it->get(stateLoop, varEl); - compilation::ICodeScopeUnit* scopeLoopUnit = function->getScopeUnit(scopeLoop); - scopeLoopUnit->bindArg(elIn, move(varEl)); - Value* elOut = scopeLoopUnit->compile(); - Value *pElOut = builder.CreateGEP(dataOut, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), stateLoop})); - builder.CreateStore(elOut, pElOut); - - //next iteration preparing - Value *stateLoopNext = builder.CreateAdd(stateLoop, llvm::ConstantInt::get(tyNum, 1)); - stateLoop->addIncoming(stateLoopNext, builder.GetInsertBlock()); - - //next iteration checks: - Value* condAfter = builder.CreateICmpSLE(stateLoopNext, rangeTo); - builder.CreateCondBr(condAfter, blockLoop, blockAfterLoop); - - //finalization: - builder.SetInsertPoint(blockAfterLoop); - - return dataOut; -} - -Value* -AdvancedInstructions::compileArrayIndex(llvm::Value* aggregate, std::vector indexes, std::string hintRetVar) { - EXPAND_CONTEXT UNUSED(function); UNUSED(scope); - - indexes.insert(indexes.begin(), llvm::ConstantInt::get(tyNum, 0)); - llvm::Value *pEl = llvm->builder.CreateGEP(aggregate, llvm::ArrayRef(indexes)); - return llvm->builder.CreateLoad(pEl, NAME("el")); -} - -Value* -AdvancedInstructions::compileStructIndex(llvm::Value* aggregate, const ExpandedType& t, const std::string& idx) { - EXPAND_CONTEXT UNUSED(scope); UNUSED(function); - - TypeUtils types(llvm); - std::vector&& fields = types.getStructFields(t); - - for (unsigned i = 0, size = fields.size(); i < size; ++i) { - if (fields.at(i) == idx) { - //dereference pointer - if (types.isPointer(t)) { - llvm::Value* addr = llvm->builder.CreateConstGEP2_32(nullptr, aggregate, 0, i); - return llvm->builder.CreateLoad(addr); - } - - return llvm->builder.CreateExtractValue(aggregate, llvm::ArrayRef{i}); - } - } - - assert(false && "not found required struct field"); - return nullptr; -} - -llvm::Value* -AdvancedInstructions::compileFold(const Expression& fold, const std::string& hintRetVar) { - EXPAND_CONTEXT - assert(fold.op == Operator::FOLD); - - //initialization: - Symbol varInSymbol = Attachments::get(fold.getOperands()[0]); - Implementation info = Query::queryImplementation(varInSymbol); - - Iterator* it = Iterator::create(context, varInSymbol); - llvm::Value* rangeBegin = it->begin(); - llvm::Value* rangeEnd = it->end(); - llvm::Value* accumInit = scope->process(fold.getOperands()[1]); - std::string varIn = fold.getOperands()[0].getValueString(); - std::string varAccum = fold.bindings[1]; - std::string varEl = fold.bindings[0]; - - llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); - std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); - - - llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold", function->raw); - llvm::BasicBlock *blockLoopBody = llvm::BasicBlock::Create(llvm->llvmContext, "fold_body", function->raw); - llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold_after", function->raw); - llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "fold_next", function->raw); - - llvm->builder.CreateBr(blockLoop); - - // * create phi - llvm->builder.SetInsertPoint(blockLoop); - llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, varAccum); - accum->addIncoming(accumInit, blockBeforeLoop); - llvm::PHINode *itLoop = llvm->builder.CreatePHI(rangeBegin->getType(), 2, "foldIt"); - itLoop->addIncoming(rangeBegin, blockBeforeLoop); - - // * loop checks - Value* condRange = llvm->builder.CreateICmpNE(itLoop, rangeEnd); - llvm->builder.CreateCondBr(condRange, blockLoopBody, blockAfterLoop); - - // * loop body - llvm->builder.SetInsertPoint(blockLoopBody); - CodeScope* scopeLoop = fold.blocks.front(); - compilation::ICodeScopeUnit* loopUnit = function->getScopeUnit(scopeLoop); - Value* elIn = it->get(itLoop); - loopUnit->bindArg(accum, move(varAccum)); - loopUnit->bindArg(elIn, move(varEl)); - Value* accumNext = loopUnit->compile(); - - // * Loop saturation checks - bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); - llvm::BasicBlock* blockSaturation = llvm->builder.GetInsertBlock(); - if (!flagSaturationTriggered){ - llvm->builder.CreateBr(blockNext); - } - - // * computing next iteration state - llvm->builder.SetInsertPoint(blockNext); - Value *itLoopNext = it->advance(itLoop); - accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); - itLoop->addIncoming(itLoopNext, llvm->builder.GetInsertBlock()); - llvm->builder.CreateBr(blockLoop); - - // * finalization: - llvm->builder.SetInsertPoint(blockAfterLoop); - if (!flagSaturationTriggered){ - return accum; - } - - llvm::PHINode* result = llvm->builder.CreatePHI(accumInit->getType(), 2); - result->addIncoming(accum, blockLoop); - result->addIncoming(accumNext, blockSaturation); - return result; -} - -llvm::Value* -AdvancedInstructions::compileFoldInf(const Expression& fold, const std::string& hintRetVar) { - EXPAND_CONTEXT - assert(fold.op == Operator::INF); - - std::string accumName = fold.bindings[0]; - llvm::Value* accumInit = scope->process(fold.getOperands()[0]); - - llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); - llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf", function->raw); - llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_next", function->raw); - llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_post", function->raw); - std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); - - llvm->builder.CreateBr(blockLoop); - - // * create phi - llvm->builder.SetInsertPoint(blockLoop); - llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, accumName); - accum->addIncoming(accumInit, blockBeforeLoop); - - // * loop body - CodeScope* scopeLoop = fold.blocks.front(); - compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); - unitLoop->bindArg(accum, move(accumName)); - Value* accumNext = unitLoop->compile(); - - // * Loop saturation checks - bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); - assert(flagSaturationTriggered); - - // * computing next iteration state - llvm->builder.SetInsertPoint(blockNext); - accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); - llvm->builder.CreateBr(blockLoop); - - // finalization: - llvm->builder.SetInsertPoint(blockAfterLoop); - return accumNext; -} - -llvm::Value* -AdvancedInstructions::compileIf(const Expression& exprIf, const std::string& hintRetVar) { - EXPAND_CONTEXT - - const Expression& condExpr = exprIf.getOperands()[0]; - llvm::IRBuilder<>& builder = llvm->builder; - assert(builder.GetInsertBlock() == scope->currentBlockRaw); - - //initialization: - llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "ifAfter", function->raw); - llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm->llvmContext, "ifTrue", function->raw); - llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm->llvmContext, "ifFalse", function->raw); - - llvm::Value* cond = scope->process(condExpr); - - builder.SetInsertPoint(blockTrue); - CodeScope* scopeTrue = exprIf.blocks.front(); - llvm::Value* resultTrue = function->getScopeUnit(scopeTrue)->compile(); - llvm::BasicBlock * blockTrueEnd = builder.GetInsertBlock(); - builder.CreateBr(blockEpilog); - - builder.SetInsertPoint(blockFalse); - CodeScope* scopeFalse = exprIf.blocks.back(); - llvm::Value* resultFalse = function->getScopeUnit(scopeFalse)->compile(); - llvm::BasicBlock * blockFalseEnd = builder.GetInsertBlock(); - builder.CreateBr(blockEpilog); - - builder.SetInsertPoint(scope->currentBlockRaw); - llvm->builder.CreateCondBr(cond, blockTrue, blockFalse); - - builder.SetInsertPoint(blockEpilog); - llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if")); - - ret->addIncoming(resultTrue, blockTrueEnd); - ret->addIncoming(resultFalse, blockFalseEnd); - - return ret; -} - -//TODO Switch: default variant no needed when all possible conditions are considered -llvm::Value* -AdvancedInstructions::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) { - EXPAND_CONTEXT UNUSED(function); - AST* root = context.pass->man->root; - llvm::IRBuilder<>& builder = llvm->builder; - assert(exprSwitch.operands.size() >= 2); - assert(exprSwitch.operands[1].op == Operator::CASE_DEFAULT && "No default case in Switch Statement"); - - int countCases = exprSwitch.operands.size() - 1; - llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); - llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); - builder.SetInsertPoint(blockEpilog); - llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch)); - llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch")); - llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext); - - builder.SetInsertPoint(blockProlog); - llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]); - llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm->llvmContext, "caseDefault", function->raw); - llvm::SwitchInst * instructionSwitch = builder.CreateSwitch( - typeinference::doAutomaticTypeConversion(conditionSwitch, typI8, builder), - blockDefault, - countCases); - - for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) { - llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(i), function->raw); - - llvm::Value* condCase = function->getScopeUnit(exprSwitch.operands[i].blocks.front())->compile(); - builder.SetInsertPoint(blockCase); - llvm::Value* resultCase = function->getScopeUnit(exprSwitch.operands[i].blocks.back())->compile(); - builder.CreateBr(blockEpilog); - - ret->addIncoming(resultCase, builder.GetInsertBlock()); - builder.SetInsertPoint(blockProlog); - instructionSwitch->addCase( - dyn_cast( - typeinference::doAutomaticTypeConversion(condCase, typI8, builder)), - blockCase); - } - - //compile default block: - builder.SetInsertPoint(blockDefault); - CodeScope* scopeDefault = exprSwitch.operands[1].blocks.front(); - llvm::Value* resultDefault = function->getScopeUnit(scopeDefault)->compile(); - builder.CreateBr(blockEpilog); - ret->addIncoming(resultDefault, builder.GetInsertBlock()); - builder.SetInsertPoint(blockEpilog); - - return ret; -} - -llvm::Value* -AdvancedInstructions::compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar) { - EXPAND_CONTEXT UNUSED(function); - AST* root = context.pass->man->root; - llvm::IRBuilder<>& builder = llvm->builder; - llvm::Type* typI8= llvm::Type::getInt8Ty(llvm->llvmContext); - const ExpandedType& typVariant = root->getType(exprSwitch.operands.at(0)); - llvm::Type* typVariantRaw = llvm->toLLVMType(typVariant); - - assert(typVariant->__operands.size() == exprSwitch.operands.size() - 1 && "Ill-formed Switch Variant"); - - int casesCount = exprSwitch.operands.size(); - llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); - llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); - builder.SetInsertPoint(blockEpilog); - llvm::Type* resultType = llvm->toLLVMType(root->getType(exprSwitch)); - llvm::PHINode *ret = builder.CreatePHI(resultType, casesCount, NAME("switch")); - - builder.SetInsertPoint(blockProlog); - llvm::Value * conditionSwitchRaw = scope->process(exprSwitch.operands.at(0)); - llvm::Value* idRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({0})); - - //Dereference preparation - const bool flagPrepareDerefence = std::any_of(typVariant->__operands.begin(), typVariant->__operands.end(), [](const TypeAnnotation& op){ - return op.isValid(); - }); - - llvm::Value* addrAsStorage = nullptr; - if (flagPrepareDerefence){ - assert(exprSwitch.bindings.size() && "Switch condition alias not found"); - llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); - llvm::Value* storageRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({1})); - addrAsStorage = llvm->builder.CreateAlloca(typStorageRaw); - llvm->builder.CreateStore(storageRaw, addrAsStorage); - } - - llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(idRaw, nullptr, casesCount); - llvm::BasicBlock* blockDefaultUndefined; - std::list::const_iterator scopeCaseIt = exprSwitch.blocks.begin(); - for (int instancesSize = exprSwitch.operands.size()-1, instId = 0; instId < instancesSize; ++instId) { - llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(instId), function->raw); - builder.SetInsertPoint(blockCase); - ICodeScopeUnit* unitCase = function->getScopeUnit(*scopeCaseIt); - const ExpandedType& instType = ExpandedType(typVariant->__operands.at(instId)); - - //Actual variant derefence - if (instType->isValid()) { - string identCondition = exprSwitch.bindings.front(); - llvm::Type* instTypeRaw = llvm->toLLVMType(instType); - llvm::Value* addrAsInst = llvm->builder.CreateBitOrPointerCast(addrAsStorage, instTypeRaw->getPointerTo()); - llvm::Value* instRaw = llvm->builder.CreateLoad(instTypeRaw, addrAsInst); - const Symbol& identSymb = unitCase->bindArg(instRaw, move(identCondition)); - Attachments::put(identSymb, instType); - } - - llvm::Value* resultCase = function->getScopeUnit(*scopeCaseIt)->compile(); - builder.CreateBr(blockEpilog); - - ret->addIncoming(resultCase, blockDefaultUndefined = builder.GetInsertBlock()); - builder.SetInsertPoint(blockProlog); - instructionSwitch->addCase(dyn_cast(llvm::ConstantInt::get(typI8, exprSwitch.operands.at(instId+1).getValueDouble())), blockCase); - ++scopeCaseIt; - } - - instructionSwitch->setDefaultDest(blockDefaultUndefined); - builder.SetInsertPoint(blockEpilog); - return ret; -} - -//TODO recognize cases to make const arrays/stored in global mem/stack alloced. -llvm::Value* -AdvancedInstructions::compileListAsSolidArray(const Expression &expr, const std::string& hintRetVar) { - EXPAND_CONTEXT UNUSED(scope); UNUSED(function); - - AST* root = context.pass->man->root; - const size_t& length = expr.getOperands().size(); - const Expression& expression = expr; - llvm::Value* zero = ConstantInt::get(tyNum, 0); - llvm::Value* one = ConstantInt::get(tyNum, 1); - - ExpandedType typAggrExpanded = root->getType(expression); - assert(typAggrExpanded->__operator == TypeOperator::LIST_ARRAY); - llvm::Type* typEl = llvm->toLLVMType(ExpandedType(typAggrExpanded->__operands[0])); - ArrayType* typAggr = (ArrayType*) llvm::ArrayType::get(typEl, length); - llvm::Value* list = llvm->builder.CreateAlloca(typAggr, ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), length, false), hintRetVar); - - const std::vector& operands = expression.getOperands(); - llvm::Value* addrOperand = llvm->builder.CreateGEP(typAggr, list, ArrayRef(std::vector{zero, zero})); - llvm->builder.CreateStore(scope->process(operands.front()), addrOperand) ; - for (auto i=++operands.begin(); i!=operands.end(); ++i){ - addrOperand = llvm->builder.CreateGEP(typEl, addrOperand, ArrayRef(std::vector{one})); - llvm->builder.CreateStore(scope->process(*i), addrOperand) ; - } - - return list; -// Value* listDest = l.builder.CreateAlloca(typList, ConstantInt::get(typI32, __size), *hintRetVar); -// l.buil1der.CreateMemCpy(listDest, listSource, __size, 16); -} - -llvm::Value* -AdvancedInstructions::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) { - EXPAND_CONTEXT UNUSED(function); UNUSED(scope); - - Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm->llvmContext)); - //ArrayType* typStr = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::I8, size+1)))); - - /* - std::vector chars; - chars.reserve(size+1); - - for (size_t i=0; i< size; ++i){ - chars[i] = ConstantInt::get(typI8, (unsigned char) data[i]); - } - chars[size] = ConstantInt::get(typI8, 0); - */ - - Value* rawData = ConstantDataArray::getString(llvm->llvmContext, data); - Value* rawPtrData = llvm->builder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 1, false)); - llvm->builder.CreateStore(rawData, rawPtrData); - return llvm->builder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar); -} - -llvm::Value* -AdvancedInstructions::compileSequence(const Expression &expr){ - EXPAND_CONTEXT UNUSED(scope); UNUSED(llvm); - - llvm::Value* result; - for(CodeScope* scope: expr.blocks){ - result = function->getScopeUnit(scope)->compile(); - } - - return result; -} - - diff --git a/cpp/src/compilation/containers.cpp b/cpp/src/compilation/containers.cpp index eb6c3f5..6a50ca4 100644 --- a/cpp/src/compilation/containers.cpp +++ b/cpp/src/compilation/containers.cpp @@ -1,208 +1,262 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: containers.cpp * Author: pgess */ /** * \file compilation/containers.h * \brief Containers compilation support. See [Containers](/d/concepts/containers/) in the Xreate's documentation. */ #include "compilation/containers.h" +#include "compilation/targetinterpretation.h" +#include "aux/expressions.h" +#include "compilation/containers/arrays.h" using namespace std; using namespace llvm; using namespace xreate; using namespace xreate::containers; - -Iterator* -Iterator::create(xreate::compilation::Context context, const xreate::Symbol& var){ - -const Implementation& data = Query::queryImplementation(var); - -switch(data.impl){ - case ON_THE_FLY: - return new IteratorForward(context, var, data.extract()); - - case SOLID: - return new IteratorForward(context, var, data.extract()); - - default: assert(true); -} - -assert(false && "Unknown declaration"); -return nullptr; -} - -llvm::Value* -IteratorForward::begin() { - switch(sourceDecl.op) { - case xreate::Operator::LIST: - { - sourceRawType = llvm::Type::getInt32Ty(llvm->llvmContext); - return llvm::ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 0); - }; - - case xreate::Operator::LIST_RANGE:{ - assert(sourceDecl.operands.size()==2); - - llvm::Value* result = sourceUnit->process(sourceDecl.operands.at(0)); - sourceRawType = result->getType(); - - return result; - }; - - default: break; +using namespace xreate::interpretation; +using namespace xreate::typehints; + +namespace xreate { namespace containers{ + +IContainersIR * +IContainersIR::create(const Expression &aggrE, const TypeAnnotation &expectedT, const compilation::Context &context){ + ExpandedType aggrT = context.pass->man->root->getType(aggrE, expectedT); + Expression aggr2E; + + if (aggrE.__state == Expression::IDENT && !aggrE.tags.size()){ + Symbol aggrS = Attachments::get(aggrE); + aggr2E = CodeScope::getDefinition(aggrS); + } else { + aggr2E = aggrE; + } + + switch(aggr2E.op){ + case Operator::LIST:{ + typehints::Array aggrHint = typehints::getHint( + aggr2E, typehints::Array{aggr2E.operands.size()} + ); + + return new ArrayIR(context, aggrT, aggrHint); } - if (linkedlist){ - llvm::Value* result = sourceUnit->process(sourceDecl); - sourceRawType = result->getType(); - return result; - } - - assert(false); -} - -llvm::Value* -IteratorForward::end(){ - switch(sourceDecl.op) { - case xreate::Operator::LIST: { - size_t idLast = sourceDecl.operands.size() - 1; - return ConstantInt::get(sourceRawType, idLast); - } - - case xreate::Operator::LIST_RANGE: { - assert(sourceDecl.operands.size() == 2); - llvm::Value* valueEndOfRange = sourceUnit->process(sourceDecl.operands.at(1)); - llvm::Value* valueConstOne = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1); - - return llvm->builder.CreateAdd(valueEndOfRange, valueConstOne); - }; + default: + typehints::Array aggrHint = typehints::getHint( + aggr2E, typehints::Array{0} + ); + assert(aggrHint.size != 0); + return new ArrayIR(context, aggrT, aggrHint); + } - default: break; - } - - //return null pointer - if (linkedlist){ - return ConstantPointerNull::getNullValue(sourceRawType); - } - - assert(false && "Unknown declaration"); - return nullptr; + assert(false); + return nullptr; } -llvm::Value* -IteratorForward::get(Value* index,const std::string& hintRetVar){ - const Expression& currentDecl = CodeScope::getDefinition(current); - - switch (currentDecl.op) { - case xreate::Operator::LIST: { - //TODO re check is it right scope(source) to compile currentDecl. Provide unittests. - llvm::Value* currentValue = sourceUnit->processSymbol(current); - return xreate::compilation::AdvancedInstructions(context).compileArrayIndex(currentValue, std::vector{index}); - }; - - case xreate::Operator::LIST_RANGE: { - return index; - }; - - case xreate::Operator::MAP: { - assert(currentDecl.getOperands().size()==1); - assert(currentDecl.bindings.size()); - assert(currentDecl.blocks.size()); - - CodeScope* scopeLoop = currentDecl.blocks.front(); - std::string varEl = currentDecl.bindings[0]; - - const Symbol& symbIn = Attachments::get(currentDecl.getOperands()[0]); - auto it = std::unique_ptr(Iterator::create(context, symbIn)); - - Value* elIn = it->get(index, varEl); - compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); - unitLoop->bindArg(elIn, std::move(varEl)); - return unitLoop->compile(); - } - - case xreate::Operator::INVALID: { - //TODO review iterator determination strategy for case of Expression::BINDING - assert(currentDecl.__state==Expression::IDENT); - - const Symbol& symbIn = Attachments::get(currentDecl); - auto it = std::unique_ptr(Iterator::create(context, symbIn)); - return it->get(index); - }; - - default: break; - } - - if (linkedlist){ - return index; - } - - assert(false && "Unknown declaration"); - return nullptr; +llvm::Value * +RecordIR::init(llvm::StructType *tyAggr){ + return llvm::UndefValue::get(tyAggr); } -llvm::Value* -IteratorForward::advance(Value* index, const std::string& hintRetVar){ - switch(sourceDecl.op) - { - case xreate::Operator::LIST: - case xreate::Operator::LIST_RANGE: - return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1), hintRetVar); - - default: break; +llvm::Value * +RecordIR::update(llvm::Value *aggrRaw, const ExpandedType &aggrT, const Expression &updE){ + InterpretationScope *scopeI12n = __context.pass->targetInterpretation->transformContext(__context); + TypesHelper helper(__context.pass->man->llvm); + const auto &fields = helper.getRecordFields(aggrT); + + std::map indexFields; + for(size_t i = 0, size = fields.size(); i < size; ++i){ + indexFields.emplace(fields[i], i); + } + + for(const auto &entry: reprListAsDict(updE)){ + unsigned keyId; + std::string keyHint; + const Expression keyE = scopeI12n->process(entry.first); + + switch(keyE.__state){ + case Expression::STRING: + keyId = indexFields.at(keyE.getValueString()); + keyHint = keyE.getValueString(); + break; + + case Expression::NUMBER: + keyId = keyE.getValueDouble(); + keyHint = aggrT->fields.at(keyId); + break; + default: + assert(false); + break; } - if (linkedlist){ - ExpandedType tySource = llvm->ast->getType(CodeScope::getDefinition(source)); - assert(tySource->__operator == TypeOperator::LIST_ARRAY && "Linked list implementation has to have ARRAY type"); - assert(tySource->__operands.size()); + const TypeAnnotation &valueT = aggrT->__operands.at(keyId); - return xreate::compilation::AdvancedInstructions(context).compileStructIndex(index, ExpandedType(TypeAnnotation(tySource->__operands.at(0))), linkedlist.fieldPointer); - } + llvm::Value *valueRaw = __context.scope->process(entry.second, keyHint, valueT); + aggrRaw = __context.pass->man->llvm->irBuilder.CreateInsertValue( + aggrRaw, + valueRaw, + keyId); + } - assert(false && "Unknown declaration"); - return nullptr; + return aggrRaw; } -//const ImplementationRec& implementation - -IteratorForward::IteratorForward(const compilation::Context& ctx, const xreate::Symbol& symbolContainer, const ImplementationRec& implementation) - : Iterator(), __length(implementation.size), llvm(ctx.pass->man->llvm) -{ - __container = ctx.function->getScopeUnit(symbolContainer.scope)->processSymbol(symbolContainer); -} - -llvm::Value* -IteratorForward::begin(){ - //0 - return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 0); -} - -llvm::Value* -IteratorForward::end(){ - //length - return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), __length); -} - -llvm::Value* -IteratorForward::get(llvm::Value* index,const std::string& hintRetVar){ - //GEP[index]] - - llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm->llvmContext); - llvm::Value* pResult = llvm->builder.CreateGEP(__container, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), index})); - return llvm->builder.CreateLoad(pResult, hintRetVar); -} +//IFwdIteratorIR* +//IFwdIteratorIR::create(xreate::compilation::Context context, const xreate::Symbol& var){ +// const Implementation& data = Query::queryImplementation(var); +// +// switch(data.impl){ +// case ON_THE_FLY: +// return new FwdIteratorIR(context, var, data.extract()); +// +// case SOLID: +// return new FwdIteratorIR(context, var, data.extract()); +// +// default: break; +//} +// +// assert(false && "Unknown declaration"); +// return nullptr; +//} +// +//llvm::Value* +//FwdIteratorIR::begin() { +// switch(sourceDecl.op) { +// case xreate::Operator::LIST: +// { +// sourceRawType = llvm::Type::getInt32Ty(llvm->llvmContext); +// return llvm::ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 0); +// }; +// +// case xreate::Operator::LIST_RANGE:{ +// assert(sourceDecl.operands.size()==2); +// +// llvm::Value* result = sourceUnit->process(sourceDecl.operands.at(0)); +// sourceRawType = result->getType(); +// +// return result; +// }; +// +// default: break; +// } +// +// if (linkedlist){ +// llvm::Value* result = sourceUnit->process(sourceDecl); +// sourceRawType = result->getType(); +// return result; +// } +// +// assert(false); +//} +// +//llvm::Value* +//FwdIteratorIR::end(){ +// switch(sourceDecl.op) { +// case xreate::Operator::LIST: { +// size_t idLast = sourceDecl.operands.size() - 1; +// return ConstantInt::get(sourceRawType, idLast); +// } +// +// case xreate::Operator::LIST_RANGE: { +// assert(sourceDecl.operands.size() == 2); +// llvm::Value* valueEndOfRange = sourceUnit->process(sourceDecl.operands.at(1)); +// llvm::Value* valueConstOne = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1); +// +// return llvm->irBuilder.CreateAdd(valueEndOfRange, valueConstOne); +// }; +// +// default: break; +// } +// +// //return null pointer +// if (linkedlist){ +// return ConstantPointerNull::getNullValue(sourceRawType); +// } +// +// assert(false && "Unknown declaration"); +// return nullptr; +//} +// +//llvm::Value* +//FwdIteratorIR::get(Value* index, const std::string& hintRetVar){ +// const Expression& currentDecl = CodeScope::getDefinition(current); +// +// switch (currentDecl.op) { +// case xreate::Operator::LIST: { +// //TODO re check is it right scope(source) to compile currentDecl. Provide unittests. +// llvm::Value* currentValue = sourceUnit->processSymbol(current); +// return xreate::compilation::ControlIR(context).compileArrayIndex(currentValue, std::vector{index}); +// }; +// +// case xreate::Operator::LIST_RANGE: { +// return index; +// }; +// +// case xreate::Operator::MAP: { +// assert(currentDecl.getOperands().size()==1); +// assert(currentDecl.bindings.size()); +// assert(currentDecl.blocks.size()); +// +// CodeScope* scopeLoop = currentDecl.blocks.front(); +// std::string varEl = currentDecl.bindings[0]; +// +// const Symbol& symbIn = Attachments::get(currentDecl.getOperands()[0]); +// auto it = std::unique_ptr(IFwdIteratorIR::create(context, symbIn)); +// +// Value* elIn = it->get(index, varEl); +// compilation::IBruteScope* unitLoop = function->getScopeUnit(scopeLoop); +// unitLoop->bindArg(elIn, std::move(varEl)); +// return unitLoop->compile(); +// } +// +// case xreate::Operator::INVALID: { +// //TODO review iterator determination strategy for case of Expression::BINDING +// assert(currentDecl.__state==Expression::IDENT); +// +// const Symbol& symbIn = Attachments::get(currentDecl); +// auto it = std::unique_ptr(IFwdIteratorIR::create(context, symbIn)); +// return it->get(index); +// }; +// +// default: break; +// } +// +// if (linkedlist){ +// return index; +// } +// +// assert(false && "Unknown declaration"); +// return nullptr; +//} +// +//llvm::Value* +//FwdIteratorIR::advance(Value* index, const std::string& hintRetVar){ +// switch(sourceDecl.op) +// { +// case xreate::Operator::LIST: +// case xreate::Operator::LIST_RANGE: +// return llvm->irBuilder.CreateAdd(index, llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm->llvmContext), 1), hintRetVar); +// +// default: break; +// } +// +// if (linkedlist){ +// ExpandedType tySource = llvm->ast->getType(CodeScope::getDefinition(source)); +// assert(tySource->__operator == TypeOperator::ARRAY && "Linked list implementation has to have ARRAY type"); +// assert(tySource->__operands.size()); +// +// return xreate::compilation::ControlIR(context).compileStructIndex(index, ExpandedType(TypeAnnotation(tySource->__operands.at(0))), {linkedlist.fieldPointer}); +// } +// +// assert(false && "Unknown declaration"); +// return nullptr; +//} -llvm::Value* -IteratorForward::advance(llvm::Value* index, const std::string& hintRetVar){ - //index + 1 +//const ImplementationRec& implementation - llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm->llvmContext); - return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(tyNum, 1), hintRetVar); -} +}} //end of xreate::containers diff --git a/cpp/src/compilation/containers.h b/cpp/src/compilation/containers.h index 893b387..4e830e8 100644 --- a/cpp/src/compilation/containers.h +++ b/cpp/src/compilation/containers.h @@ -1,100 +1,103 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: containers.h * Author: pgess */ #ifndef CODEINSTRUCTIONS_H #define CODEINSTRUCTIONS_H #include "ast.h" #include "llvmlayer.h" #include "pass/compilepass.h" -#include "compilation/advancedinstructions.h" +#include "compilation/control.h" #include "query/containers.h" +#include "analysis/typehints.h" -namespace xreate { - namespace containers { +namespace xreate { namespace containers { -using namespace llvm; +class IFwdIteratorIR; -/** +class IContainersIR{ +public: + static IContainersIR *create( + const Expression &aggrE, + const TypeAnnotation &expectedT, + const compilation::Context &context); + + virtual llvm::Value *init(const std::string &hintAlias = "") = 0; + virtual llvm::Value *update(llvm::Value *aggrRaw, const Expression &updE, const std::string &hintAlias) = 0; + virtual IFwdIteratorIR* getFwdIterator() = 0; + virtual ~IContainersIR(){} +}; + +class RecordIR{ +public: + RecordIR(const compilation::Context& context): __context(context){} + llvm::Value* init(llvm::StructType* tyAggr); + llvm::Value* update(llvm::Value* aggrRaw, const ExpandedType& aggrT, const Expression& updE); + +private: + compilation::Context __context; +}; + +/** * \brief A factory to create a concrete iterator based on the solution provided by xreate::containers::Query * \sa xreate::containers::Query */ -class Iterator{ +class IFwdIteratorIR{ public : - virtual llvm::Value* begin() =0; - virtual llvm::Value* end() = 0; - virtual llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") = 0; - virtual llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="")=0; - virtual ~Iterator(){}; + virtual ~IFwdIteratorIR(){}; - static Iterator* create(xreate::compilation::Context context, const xreate::Symbol& var); + virtual llvm::Value* begin() = 0; + virtual llvm::Value* end() = 0; + virtual llvm::Value* get(llvm::Value* aggrRaw, llvm::Value *idxRaw, const std::string &hintRetVar="") = 0; + virtual llvm::Value* advance(llvm::Value* idxRaw, const std::string& hintAlias="") = 0; }; template -class IteratorForward; +class FwdIteratorIR; /** \brief The lazy container implementation. * * Represents computation on the fly. - * \sa xreate::containers::Iterator, \sa xreate::containers::Query + * \sa xreate::containers::IFwdIteratorIR, \sa xreate::containers::Query */ -template<> -class IteratorForward : public Iterator { -private: - LLVMLayer* llvm; - const xreate::Symbol current; - const Symbol source; - const ImplementationLinkedList linkedlist; - const CodeScope* const sourceScope; - //TODO initialize and mark as const (three fields) - compilation::ICodeScopeUnit* sourceUnit; - compilation::IFunctionUnit* function; //TODO is used somewhere? - const Expression& sourceDecl; - compilation::Context context; - llvm::Type* sourceRawType =nullptr; - -public: - IteratorForward(const compilation::Context& ctx, const xreate::Symbol& s, const ImplementationRec& implementation) - : llvm(ctx.pass->man->llvm), - current(s), - source(implementation.source), - linkedlist(source), - sourceScope(source.scope), - sourceUnit(ctx.function->getScopeUnit(source.scope)), - sourceDecl(CodeScope::getDefinition(source)), - context(ctx) - {} - - llvm::Value* begin() override; - llvm::Value* end() override; - llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") override; - llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="") override; -}; - -/** \brief The contiguous container implementation. - * - * Represents contiguous in memory(array) implementation. - * \sa xreate::containers::Iterator, \sa xreate::containers::Query - */ -template<> -class IteratorForward: public Iterator{ - size_t __length; - llvm::Value* __container; - LLVMLayer* llvm; - -public: - IteratorForward(const compilation::Context& ctx, const xreate::Symbol& symbolContainer, const ImplementationRec& implementation); - llvm::Value* begin() override; - llvm::Value* end() override; - llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") override; - llvm::Value* advance(llvm::Value* index, const std::string& hintRetVar="") override; -}; +//template<> +//class FwdIteratorIR : public IFwdIteratorIR { +//private: +// LLVMLayer* llvm; +// const xreate::Symbol current; +// const Symbol source; +// const ImplementationLinkedList linkedlist; +// const CodeScope* const sourceScope; +// //TODO initialize and mark as const (three fields) +// compilation::IBruteScope* sourceUnit; +// compilation::IBruteFunction* function; //TODO is used somewhere? +// const Expression& sourceDecl; +// compilation::Context context; +// llvm::Type* sourceRawType =nullptr; +// +//public: +// FwdIteratorIR(const compilation::Context& ctx, const xreate::Symbol& s, const ImplementationRec& implementation) +// : llvm(ctx.pass->man->llvm), +// current(s), +// source(implementation.source), +// linkedlist(source), +// sourceScope(source.scope), +// sourceUnit(ctx.function->getScopeUnit(source.scope)), +// sourceDecl(CodeScope::getDefinition(source)), +// context(ctx) +// {} +// +// llvm::Value* begin() override; +// llvm::Value* end() override; +// llvm::Value* get(llvm::Value* index,const std::string& hintRetVar="") override; +// llvm::Value* advance(llvm::Value* idxRaw, const std::string& hintAlias="") override; +//}; }} #endif //CODEINSTRUCTIONS_H diff --git a/cpp/src/compilation/containers/arrays.cpp b/cpp/src/compilation/containers/arrays.cpp new file mode 100644 index 0000000..ea287bd --- /dev/null +++ b/cpp/src/compilation/containers/arrays.cpp @@ -0,0 +1,106 @@ +// +// Created by pgess on 27/03/2020. +// + +#include "compilation/containers/arrays.h" +#include "aux/expressions.h" + +using namespace std; +using namespace llvm; + +namespace xreate { namespace containers{ + +llvm::ArrayType* +ArrayIR::getRawT(){ + assert(__aggrT->__operator == TypeOperator::ARRAY); + assert(__aggrT->__operands.size() == 1); + + LLVMLayer* llvm = __context.pass->man->llvm; + llvm::Type* elRawT = llvm->toLLVMType(ExpandedType(__aggrT->__operands.at(0))); + + return llvm::ArrayType::get(elRawT, __hints.size); +} + +llvm::Value* +ArrayIR::init(const string& hintAlias){ + LLVMLayer* llvm = __context.pass->man->llvm; + TypesHelper helper(llvm); + llvm::ArrayType* aggrRawT = getRawT(); + + //llvm::Value* aggrLengthRaw = ConstantInt::get(helper.getPreferredIntTy(), aggrInfo.size); + llvm::Value* aggrRaw = llvm->irBuilder.CreateAlloca(aggrRawT, nullptr, hintAlias); + + return aggrRaw; +} + +llvm::Value* +ArrayIR::update(llvm::Value* aggrRaw, const Expression& updE, const std::string& hintAlias) { + LLVMLayer* llvm = __context.pass->man->llvm; + TypesHelper helper(llvm); + llvm::IntegerType* intT = helper.getPreferredIntTy(); + llvm::Value* idxZeroRaw = ConstantInt::get(intT, 0); + llvm::ArrayType* aggrRawT = getRawT(); + const TypeAnnotation& elT = __aggrT->__operands.at(0); + //llvm::Type* elTRaw = llvm->toLLVMType(ExpandedType(aggrT->__operands.at(0))); + + for (const auto& entry: reprListAsDict(updE)){ + llvm::Value* keyRaw = __context.scope->process(entry.first); + llvm::Value* elRaw = __context.scope->process(entry.second, "", elT); + + llvm::Value* elLoc = llvm->irBuilder.CreateGEP( + aggrRawT, aggrRaw, ArrayRef(std::vector{idxZeroRaw, keyRaw})); + llvm->irBuilder.CreateStore(elRaw, elLoc) ; + } + + return aggrRaw; +} + +llvm::Value* +ArrayIR::get(llvm::Value* aggrRaw, std::vector idxL, const std::string& hintAlias) { + LLVMLayer* llvm = __context.pass->man->llvm; + TypesHelper helper(llvm); + llvm::IntegerType* intT = helper.getPreferredIntTy(); + llvm::Value* zeroRaw = ConstantInt::get(intT, 0); + + idxL.insert(idxL.begin(), zeroRaw); + llvm::Value *pEl = llvm->irBuilder.CreateGEP(aggrRaw, llvm::ArrayRef(idxL)); + return llvm->irBuilder.CreateLoad(pEl, hintAlias); +} + +IFwdIteratorIR* ArrayIR::getFwdIterator(){ + return new FwdIteratorIR(this); +} + +llvm::Value * +FwdIteratorIR::begin(){ + TypesHelper helper(__arraysIR->__context.pass->man->llvm); + llvm::IntegerType* intT = helper.getPreferredIntTy(); + + return llvm::ConstantInt::get(intT, 0); +} + +llvm::Value * +FwdIteratorIR::end(){ + TypesHelper helper(__arraysIR->__context.pass->man->llvm); + llvm::IntegerType* intT = helper.getPreferredIntTy(); + size_t size = __arraysIR->__hints.size; + + return llvm::ConstantInt::get(intT, size); +} + +llvm::Value * +FwdIteratorIR::get(llvm::Value* aggrRaw, llvm::Value *idxRaw, const std::string &hintAlias){ + return __arraysIR->get(aggrRaw, {idxRaw}, hintAlias); +} + +llvm::Value * +FwdIteratorIR::advance(llvm::Value *idxRaw, const std::string &hintAlias){ + LLVMLayer* llvm = __arraysIR->__context.pass->man->llvm; + TypesHelper helper(llvm); + llvm::IntegerType* intT = helper.getPreferredIntTy(); + llvm::Value* cnstOneRaw = llvm::ConstantInt::get(intT, 1); + + return llvm->irBuilder.CreateAdd(idxRaw, cnstOneRaw, hintAlias); +} + +}} //xreate::containers \ No newline at end of file diff --git a/cpp/src/compilation/containers/arrays.h b/cpp/src/compilation/containers/arrays.h new file mode 100644 index 0000000..3401940 --- /dev/null +++ b/cpp/src/compilation/containers/arrays.h @@ -0,0 +1,53 @@ +// +// Created by pgess on 27/03/2020. +// + +#ifndef XREATE_ARRAYSIR_H +#define XREATE_ARRAYSIR_H + +#include "compilation/containers.h" + +namespace xreate { namespace containers{ + +class ArrayIR; + +/** \brief The contiguous container implementation. + * + * Represents contiguous in memory(array) implementation. + * \sa xreate::containers::IFwdIteratorIR, \sa xreate::containers::Query + */ +template<> +class FwdIteratorIR: public IFwdIteratorIR { + ArrayIR* __arraysIR; + +public: + FwdIteratorIR(ArrayIR* arraysIR): __arraysIR(arraysIR) {}; + + virtual llvm::Value* begin() override; + virtual llvm::Value* end() override; + virtual llvm::Value* get(llvm::Value* aggrRaw, llvm::Value *idxRaw, const std::string &hintRetVar="") override; + virtual llvm::Value* advance(llvm::Value* idxRaw, const std::string& hintAlias="") override; +}; + +class ArrayIR : public IContainersIR{ + friend class FwdIteratorIR; + +public: + ArrayIR(const compilation::Context &context, const ExpandedType &aggrT, const typehints::Array &hints) + : __context(context), __aggrT(aggrT), __hints(hints){} + + virtual llvm::Value *init(const std::string &hintAlias = "") override; + virtual llvm::Value *update(llvm::Value *aggrRaw, const Expression &updE, const std::string &hintAlias) override; + virtual IFwdIteratorIR* getFwdIterator() override; + llvm::Value *get(llvm::Value *aggrRaw, std::vector idxL, const std::string &hintAlias); + llvm::ArrayType *getRawT(); + +private: + compilation::Context __context; + ExpandedType __aggrT; + typehints::Array __hints; +}; + +}} // xreate::containers + +#endif //XREATE_ARRAYSIR_H diff --git a/cpp/src/compilation/control.cpp b/cpp/src/compilation/control.cpp new file mode 100644 index 0000000..c300b7e --- /dev/null +++ b/cpp/src/compilation/control.cpp @@ -0,0 +1,441 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * File: InstructionsAdvanced.cpp + * Author: pgess + * + * Created on June 26, 2016, 6:00 PM + */ + +#include "analysis/typeinference.h" +#include "compilation/control.h" +#include "compilation/containers.h" +#include "compilation/transformersaturation.h" + +#include "query/containers.h" +#include "llvmlayer.h" +#include "ast.h" + +using namespace std; +using namespace llvm; +using namespace xreate; +using namespace xreate::containers; +using namespace xreate::compilation; + +#define NAME(x) (hintRetVar.empty()? x : hintRetVar) +#define UNUSED(x) (void)(x) +#define EXPAND_CONTEXT \ + LLVMLayer* llvm = context.pass->man->llvm; \ + compilation::IBruteScope* scope = context.scope; \ + compilation::IBruteFunction* function = context.function; + +ControlIR::ControlIR(compilation::Context ctx) +: context(ctx), tyNum(static_cast (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Int))))) { +} + +llvm::Value* +ControlIR::compileMapSolidOutput(const Expression &expr, const std::string hintRetVar) { + assert(false); return nullptr; + EXPAND_CONTEXT UNUSED(scope); + +// //initializationcompileListAsSolidArray +// Symbol symbolIn = Attachments::get(expr.getOperands()[0]); +// +// ImplementationRec implIn = containers::Query::queryImplementation(symbolIn).extract(); // impl of input list +// size_t size = implIn.size; +// CodeScope* scopeLoop = expr.blocks.front(); +// std::string varEl = scopeLoop->__bindings[0]; +// +// IFwdIteratorIR* it = IFwdIteratorIR::create(context, symbolIn); +// llvm::Value *rangeFrom = it->begin(); +// llvm::Value *rangeTo = it->end(); +// +// //definitions +// ArrayType* tyNumArray = nullptr; //(ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::Int, size)))); +// llvm::IRBuilder<> &builder = llvm->irBuilder; +// +// llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "loop", function->raw); +// llvm::BasicBlock *blockBeforeLoop = builder.GetInsertBlock(); +// llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "postloop", function->raw); +// Value* dataOut = llvm->irBuilder.CreateAlloca(tyNumArray, ConstantInt::get(tyNum, size), NAME("map")); +// +// // * initial check +// Value* condBefore = builder.CreateICmpSLE(rangeFrom, rangeTo); +// builder.CreateCondBr(condBefore, blockLoop, blockAfterLoop); +// +// // create PHI: +// builder.SetInsertPoint(blockLoop); +// llvm::PHINode *stateLoop = builder.CreatePHI(tyNum, 2, "mapIt"); +// stateLoop->addIncoming(rangeFrom, blockBeforeLoop); +// +// // loop body: +// Value* elIn = it->get(stateLoop, varEl); +// compilation::IBruteScope* scopeLoopUnit = function->getScopeUnit(scopeLoop); +// scopeLoopUnit->bindArg(elIn, move(varEl)); +// Value* elOut = scopeLoopUnit->compile(); +// Value *pElOut = builder.CreateGEP(dataOut, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), stateLoop})); +// builder.CreateStore(elOut, pElOut); +// +// //next iteration preparing +// Value *stateLoopNext = builder.CreateAdd(stateLoop, llvm::ConstantInt::get(tyNum, 1)); +// stateLoop->addIncoming(stateLoopNext, builder.GetInsertBlock()); +// +// //next iteration checks: +// Value* condAfter = builder.CreateICmpSLE(stateLoopNext, rangeTo); +// builder.CreateCondBr(condAfter, blockLoop, blockAfterLoop); +// +// //finalization: +// builder.SetInsertPoint(blockAfterLoop); +// +// return dataOut; +} + +Value* +ControlIR::compileStructIndex(llvm::Value* aggregate, ExpandedType aggrT, const list& indices) { + EXPAND_CONTEXT UNUSED(scope); UNUSED(function); + TypesHelper types(llvm); + llvm::Value* result = aggregate; + assert(indices.size()); + + for (const string& indexField: indices){ + const std::vector& tyfields = types.getRecordFields(aggrT); + unsigned idx = -1; bool flagFound = false; + for (unsigned i = 0, size = tyfields.size(); i < size; ++i) { + if (tyfields.at(i) == indexField) { + idx = i; flagFound = true; break; + } + } + + if (flagFound){ + result = llvm->irBuilder.CreateExtractValue(result, llvm::ArrayRef{idx}); + aggrT = typeinference::getSubtype(aggrT, indexField); + } else { + assert(false && "not found required struct field"); + } + } + + return result; + +//dereference pointer +//if (types.isPointerT(t)) { +// llvm::Value* addr = llvm->irBuilder.CreateConstGEP2_32(nullptr, aggregate, 0, i); +// return llvm->irBuilder.CreateLoad(addr); +//} +} + +llvm::Value* +ControlIR::compileFold(const Expression& loopE, const std::string& hintAlias) { + EXPAND_CONTEXT + assert(loopE.op == Operator::FOLD); + AST* ast = context.pass->man->root; + + //initialization: + const Expression aggrE = loopE.getOperands().at(0); + llvm::Value* aggrRaw = context.scope->process(aggrE); + std::unique_ptr containerIR(IContainersIR::create(aggrE, TypeAnnotation(), context)); + IFwdIteratorIR* aggrItIR = containerIR->getFwdIterator(); + llvm::Value* idxBeginRaw = aggrItIR->begin(); + llvm::Value* idxEndRaw = aggrItIR->end(); + ExpandedType loopT = ast->getType(loopE); + std::string elAlias = loopE.bindings[0]; + std::string accumAlias = loopE.bindings[1]; + const Expression& accumE = loopE.getOperands().at(1); + ExpandedType accumT = ast->getType(accumE, loopT.get()); + llvm::Type* accumRawT = llvm->toLLVMType(accumT); + llvm::Value* accumInitRaw = scope->process(accumE, accumAlias, accumT.get()); + + llvm::BasicBlock *blockBeforeLoop = llvm->irBuilder.GetInsertBlock(); + std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); + + llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold", function->raw); + llvm::BasicBlock *blockLoopBody = llvm::BasicBlock::Create(llvm->llvmContext, "fold_body", function->raw); + llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "fold_after", function->raw); + llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "fold_next", function->raw); + + llvm->irBuilder.CreateBr(blockLoop); + + // * create phi + llvm->irBuilder.SetInsertPoint(blockLoop); + llvm::PHINode *accum = llvm->irBuilder.CreatePHI(accumRawT, 2, accumAlias); + accum->addIncoming(accumInitRaw, blockBeforeLoop); + llvm::PHINode *idxCurrentRaw = llvm->irBuilder.CreatePHI(idxBeginRaw->getType(), 2, "foldIt"); + idxCurrentRaw->addIncoming(idxBeginRaw, blockBeforeLoop); + + // * loop checks + Value* condRange = llvm->irBuilder.CreateICmpNE(idxCurrentRaw, idxEndRaw); + llvm->irBuilder.CreateCondBr(condRange, blockLoopBody, blockAfterLoop); + + // * loop body + llvm->irBuilder.SetInsertPoint(blockLoopBody); + CodeScope* scopeLoop = loopE.blocks.front(); + compilation::IBruteScope* loopUnit = function->getScopeUnit(scopeLoop); + Value* elIn = aggrItIR->get(aggrRaw, idxCurrentRaw); + loopUnit->bindArg(accum, move(accumAlias)); + loopUnit->bindArg(elIn, move(elAlias)); + Value* accumNext = loopUnit->compile(); + + // * Loop saturation checks + bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); + llvm::BasicBlock* blockSaturation = llvm->irBuilder.GetInsertBlock(); + if (!flagSaturationTriggered){ + llvm->irBuilder.CreateBr(blockNext); + } + + // * computing next iteration state + llvm->irBuilder.SetInsertPoint(blockNext); + Value *itLoopNext = aggrItIR->advance(idxCurrentRaw); + accum->addIncoming(accumNext, llvm->irBuilder.GetInsertBlock()); + idxCurrentRaw->addIncoming(itLoopNext, llvm->irBuilder.GetInsertBlock()); + llvm->irBuilder.CreateBr(blockLoop); + + // * finalization: + llvm->irBuilder.SetInsertPoint(blockAfterLoop); + if (!flagSaturationTriggered){ + return accum; + } + + llvm::PHINode* result = llvm->irBuilder.CreatePHI(accumRawT, 2, hintAlias); + result->addIncoming(accum, blockLoop); + result->addIncoming(accumNext, blockSaturation); + + return result; +} + +llvm::Value* +ControlIR::compileFoldInf(const Expression& fold, const std::string& hintRetVar) { + EXPAND_CONTEXT + assert(fold.op == Operator::FOLD_INF); + + std::string accumName = fold.bindings[0]; + llvm::Value* accumInit = scope->process(fold.getOperands()[0]); + + llvm::BasicBlock *blockBeforeLoop = llvm->irBuilder.GetInsertBlock(); + llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf", function->raw); + llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_next", function->raw); + llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm->llvmContext, "foldinf_post", function->raw); + std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); + + llvm->irBuilder.CreateBr(blockLoop); + + // * create phi + llvm->irBuilder.SetInsertPoint(blockLoop); + llvm::PHINode *accum = llvm->irBuilder.CreatePHI(accumInit->getType(), 2, accumName); + accum->addIncoming(accumInit, blockBeforeLoop); + + // * loop body + CodeScope* scopeLoop = fold.blocks.front(); + compilation::IBruteScope* unitLoop = function->getScopeUnit(scopeLoop); + unitLoop->bindArg(accum, move(accumName)); + Value* accumNext = unitLoop->compile(); + + // * Loop saturation checks + bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); + assert(flagSaturationTriggered); + + // * computing next iteration state + llvm->irBuilder.SetInsertPoint(blockNext); + accum->addIncoming(accumNext, llvm->irBuilder.GetInsertBlock()); + llvm->irBuilder.CreateBr(blockLoop); + + // finalization: + llvm->irBuilder.SetInsertPoint(blockAfterLoop); + return accumNext; +} + +llvm::Value* +ControlIR::compileIf(const Expression& exprIf, const std::string& hintRetVar) { + EXPAND_CONTEXT + + const Expression& condExpr = exprIf.getOperands()[0]; + llvm::IRBuilder<>& builder = llvm->irBuilder; + assert(builder.GetInsertBlock() == scope->currentBlockRaw); + + //initialization: + llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "ifAfter", function->raw); + llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm->llvmContext, "ifTrue", function->raw); + llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm->llvmContext, "ifFalse", function->raw); + + llvm::Value* cond = scope->process(condExpr); + + builder.SetInsertPoint(blockTrue); + CodeScope* scopeTrue = exprIf.blocks.front(); + llvm::Value* resultTrue = function->getScopeUnit(scopeTrue)->compile(); + llvm::BasicBlock * blockTrueEnd = builder.GetInsertBlock(); + builder.CreateBr(blockEpilog); + + builder.SetInsertPoint(blockFalse); + CodeScope* scopeFalse = exprIf.blocks.back(); + llvm::Value* resultFalse = function->getScopeUnit(scopeFalse)->compile(); + llvm::BasicBlock * blockFalseEnd = builder.GetInsertBlock(); + builder.CreateBr(blockEpilog); + + builder.SetInsertPoint(scope->currentBlockRaw); + llvm->irBuilder.CreateCondBr(cond, blockTrue, blockFalse); + + builder.SetInsertPoint(blockEpilog); + llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if")); + + ret->addIncoming(resultTrue, blockTrueEnd); + ret->addIncoming(resultFalse, blockFalseEnd); + + return ret; +} + +//TODO Switch: default variant no needed when all possible conditions are considered +llvm::Value* +ControlIR::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) { + EXPAND_CONTEXT UNUSED(function); + AST* root = context.pass->man->root; + llvm::IRBuilder<>& builder = llvm->irBuilder; + assert(exprSwitch.operands.size() >= 2); + assert(exprSwitch.operands[1].op == Operator::CASE_DEFAULT && "No default case in Switch Statement"); + + int countCases = exprSwitch.operands.size() - 1; + llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); + llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); + builder.SetInsertPoint(blockEpilog); + llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch)); + llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch")); + llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext); + + builder.SetInsertPoint(blockProlog); + llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]); + llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm->llvmContext, "caseDefault", function->raw); + llvm::SwitchInst * instructionSwitch = builder.CreateSwitch( + typeinference::doAutomaticTypeConversion(conditionSwitch, typI8, builder), + blockDefault, + countCases); + + for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) { + llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(i), function->raw); + + llvm::Value* condCase = function->getScopeUnit(exprSwitch.operands[i].blocks.front())->compile(); + builder.SetInsertPoint(blockCase); + llvm::Value* resultCase = function->getScopeUnit(exprSwitch.operands[i].blocks.back())->compile(); + builder.CreateBr(blockEpilog); + + ret->addIncoming(resultCase, builder.GetInsertBlock()); + builder.SetInsertPoint(blockProlog); + instructionSwitch->addCase( + dyn_cast( + typeinference::doAutomaticTypeConversion(condCase, typI8, builder)), + blockCase); + } + + //compile default block: + builder.SetInsertPoint(blockDefault); + CodeScope* scopeDefault = exprSwitch.operands[1].blocks.front(); + llvm::Value* resultDefault = function->getScopeUnit(scopeDefault)->compile(); + builder.CreateBr(blockEpilog); + ret->addIncoming(resultDefault, builder.GetInsertBlock()); + builder.SetInsertPoint(blockEpilog); + + return ret; +} + +llvm::Value* +ControlIR::compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar) { + EXPAND_CONTEXT UNUSED(function); + AST* root = context.pass->man->root; + llvm::IRBuilder<>& builder = llvm->irBuilder; + llvm::Type* typI8= llvm::Type::getInt8Ty(llvm->llvmContext); + const ExpandedType& typVariant = root->getType(exprSwitch.operands.at(0)); + llvm::Type* typVariantRaw = llvm->toLLVMType(typVariant); + + assert(typVariant->__operands.size() == exprSwitch.operands.size() - 1 && "Ill-formed Switch Variant"); + + int casesCount = exprSwitch.operands.size(); + llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); + llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm->llvmContext, "switchAfter", function->raw); + builder.SetInsertPoint(blockEpilog); + llvm::Type* resultType = llvm->toLLVMType(root->getType(exprSwitch)); + llvm::PHINode *ret = builder.CreatePHI(resultType, casesCount, NAME("switch")); + + builder.SetInsertPoint(blockProlog); + llvm::Value * conditionSwitchRaw = scope->process(exprSwitch.operands.at(0)); + llvm::Value* idRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({0})); + + //Dereference preparation + const bool flagPrepareDerefence = std::any_of(typVariant->__operands.begin(), typVariant->__operands.end(), [](const TypeAnnotation& op){ + return op.isValid(); + }); + + llvm::Value* addrAsStorage = nullptr; + if (flagPrepareDerefence){ + assert(exprSwitch.bindings.size() && "Switch condition alias not found"); + llvm::Type* typStorageRaw = llvm::cast(typVariantRaw)->getElementType(1); + llvm::Value* storageRaw = builder.CreateExtractValue(conditionSwitchRaw, llvm::ArrayRef({1})); + addrAsStorage = llvm->irBuilder.CreateAlloca(typStorageRaw); + llvm->irBuilder.CreateStore(storageRaw, addrAsStorage); + } + + llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(idRaw, nullptr, casesCount); + llvm::BasicBlock* blockDefaultUndefined; + std::list::const_iterator scopeCaseIt = exprSwitch.blocks.begin(); + for (int instancesSize = exprSwitch.operands.size()-1, instId = 0; instId < instancesSize; ++instId) { + llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm->llvmContext, "case" + std::to_string(instId), function->raw); + builder.SetInsertPoint(blockCase); + IBruteScope* unitCase = function->getScopeUnit(*scopeCaseIt); + const ExpandedType& instType = ExpandedType(typVariant->__operands.at(instId)); + + //Actual variant derefence + if (instType->isValid()) { + string identCondition = exprSwitch.bindings.front(); + llvm::Type* instTypeRaw = llvm->toLLVMType(instType); + llvm::Value* addrAsInst = llvm->irBuilder.CreateBitOrPointerCast(addrAsStorage, instTypeRaw->getPointerTo()); + llvm::Value* instRaw = llvm->irBuilder.CreateLoad(instTypeRaw, addrAsInst); + const Symbol& identSymb = unitCase->bindArg(instRaw, move(identCondition)); + Attachments::put(identSymb, instType); + } + + llvm::Value* resultCase = function->getScopeUnit(*scopeCaseIt)->compile(); + builder.CreateBr(blockEpilog); + + ret->addIncoming(resultCase, blockDefaultUndefined = builder.GetInsertBlock()); + builder.SetInsertPoint(blockProlog); + instructionSwitch->addCase(dyn_cast(llvm::ConstantInt::get(typI8, exprSwitch.operands.at(instId+1).getValueDouble())), blockCase); + ++scopeCaseIt; + } + + instructionSwitch->setDefaultDest(blockDefaultUndefined); + builder.SetInsertPoint(blockEpilog); + return ret; +} + +llvm::Value* +ControlIR::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) { + EXPAND_CONTEXT UNUSED(function); UNUSED(scope); + + Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm->llvmContext)); + //ArrayType* typStr = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::I8, size+1)))); + + /* + std::vector chars; + chars.reserve(size+1); + + for (size_t i=0; i< size; ++i){ + chars[i] = ConstantInt::get(typI8, (unsigned char) data[i]); + } + chars[size] = ConstantInt::get(typI8, 0); + */ + + Value* rawData = ConstantDataArray::getString(llvm->llvmContext, data); + Value* rawPtrData = llvm->irBuilder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm->llvmContext), 1, false)); + llvm->irBuilder.CreateStore(rawData, rawPtrData); + return llvm->irBuilder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar); +} + +llvm::Value* +ControlIR::compileSequence(const Expression &expr){ + EXPAND_CONTEXT UNUSED(scope); UNUSED(llvm); + + llvm::Value* result; + for(CodeScope* scope: expr.blocks){ + result = function->getScopeUnit(scope)->compile(); + } + + return result; +} + + diff --git a/cpp/src/compilation/advancedinstructions.h b/cpp/src/compilation/control.h similarity index 76% rename from cpp/src/compilation/advancedinstructions.h rename to cpp/src/compilation/control.h index 4826c2f..556bc09 100644 --- a/cpp/src/compilation/advancedinstructions.h +++ b/cpp/src/compilation/control.h @@ -1,79 +1,73 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * - * File: AdvancedInstructions.h + * File: ControlIR.h * Author: pgess * * Created on June 26, 2016, 6:00 PM */ /** * \file advancedinstructions.h * \brief Compound statements compilation */ #ifndef INSTRUCTIONSADVANCED_H #define INSTRUCTIONSADVANCED_H #include "ast.h" #include "llvmlayer.h" #include "pass/compilepass.h" #include namespace xreate { namespace compilation { /** \brief Advanced compilation primitives */ -class AdvancedInstructions { +class ControlIR { public: - AdvancedInstructions(compilation::Context ctx); - - /** \brief Array subscript access operator compilation*/ - llvm::Value* compileArrayIndex(llvm::Value* aggregate, std::vector indexes, std::string ident = ""); + ControlIR(compilation::Context ctx); /** \brief Struct field access operator compilation*/ - llvm::Value* compileStructIndex(llvm::Value* aggregate, const ExpandedType& t, const std::string& idx); + llvm::Value* compileStructIndex(llvm::Value* aggregate, ExpandedType aggrT, const std::list& indices); /* * - map Computation -> Llvm_Array: Prohibited, we do not know a result size * - map Llvm_Array -> Computation: considered in `compileGetElement` * - map Llvm_Array -> Llvm_Array considered by this method */ /** \brief `loop map` statement compilation*/ llvm::Value* compileMapSolidOutput(const Expression &expr, const std::string hintRetVar = ""); /** \brief `loop fold` statement compilation*/ - llvm::Value* compileFold(const Expression& fold, const std::string& ident=""); + llvm::Value* compileFold(const Expression& loopE, const std::string& hintAlias=""); /** \brief `loop` statement compilation*/ llvm::Value* compileFoldInf(const Expression& fold, const std::string& ident=""); /** \brief `if` statement compilation*/ llvm::Value* compileIf(const Expression& exprIf, const std::string& ident); /** \brief `switch` statement compilation*/ llvm::Value* compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar); /** \brief `switch` statement compilation*/ llvm::Value* compileSwitchVariant(const Expression& exprSwitch, const std::string& hintRetVar); /** \brief `switch variant` statement compilation*/ llvm::Value* compileConstantStringAsPChar(const std::string &data, const std::string& hintRetVar); - /** \brief Contiguous memory list implementation*/ - llvm::Value* compileListAsSolidArray(const Expression &expr, const std::string& hintRetVar); - /** \brief `seq` statement compilation */ llvm::Value* compileSequence(const Expression &expr); private: compilation::Context context; llvm::IntegerType* const tyNum; }; }} #endif /* INSTRUCTIONSADVANCED_H */ diff --git a/cpp/src/compilation/scopedecorators.h b/cpp/src/compilation/decorators.h similarity index 54% rename from cpp/src/compilation/scopedecorators.h rename to cpp/src/compilation/decorators.h index b84b9ae..87d4934 100644 --- a/cpp/src/compilation/scopedecorators.h +++ b/cpp/src/compilation/decorators.h @@ -1,188 +1,237 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: scopedecorators.h * Author: pgess * * Created on February 24, 2017, 11:35 AM */ /** * \file scopedecorators.h - * \brief Basic code block compilation xreate::compilation::ICodeScopeUnit decorators + * \brief Basic code block compilation xreate::compilation::IBruteScope decorators */ #ifndef SCOPEDECORATORS_H #define SCOPEDECORATORS_H #include "ast.h" -#include "compilation/targetinterpretation.h" -#include "compilation/versions.h" #include "compilation/transformations.h" -#include "compilation/polymorph.h" -#include "compilation/latex.h" #include "analysis/typeinference.h" +#include "compilation/demand.h" +#include "compilation/polymorph.h" +#include "compilation/targetinterpretation.h" + +#ifndef XREATE_CONFIG_MIN + #include "compilation/versions.h" + #include "compilation/polymorph.h" +#endif + #include -namespace xreate { - +namespace xreate { + class CompilePass; namespace compilation { -class ICodeScopeUnit; -class IFunctionUnit; +class IBruteScope; +class IBruteFunction; /**\brief Provides caching ability for code scope compilation - * \extends xreate::compilation::ICodeScopeUnit + * \extends xreate::compilation::IBruteScope */ template class CachedScopeDecorator: public Parent{ typedef CachedScopeDecorator SELF; public: - CachedScopeDecorator(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} + CachedScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} Symbol bindArg(llvm::Value* value, std::string&& alias) { //ensure existence of an alias assert(Parent::scope->__identifiers.count(alias)); //memorize new value for an alias ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE}; __rawVars[id] = value; - + return Symbol{id, Parent::scope}; } void bindArg(llvm::Value* value, const ScopedSymbol& s) { __rawVars[s] = value; } - - llvm::Value* compile(const std::string& hintBlockDecl="") override{ + + llvm::Value* compile(const std::string& aliasBlock="") override{ if (__rawVars.count(ScopedSymbol::RetSymbol)){ return __rawVars[ScopedSymbol::RetSymbol]; } - - return Parent::compile(hintBlockDecl); + + return Parent::compile(aliasBlock); } - llvm::Value* + llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar) override{ const CodeScope* scope = s.scope; SELF* self = dynamic_cast(Parent::function->getScopeUnit(scope)); if (self->__rawVars.count(s.identifier)){ return self->__rawVars[s.identifier]; } //Declaration could be overriden /* Expression declaration = CodeScope::getDefinition(s, true); if (!declaration.isDefined()){ assert(__declarationsOverriden.count(s.identifier)); declaration = __declarationsOverriden[s.identifier]; } else { (false); //in case of binding there should be raws provided. } } */ llvm::Value* resultRaw = Parent::processSymbol(s, hintRetVar); self->__rawVars.emplace(s.identifier, resultRaw); return resultRaw; } - void + void overrideDeclarations(std::list> bindings){ reset(); - + for (auto entry: bindings){ SELF* self = dynamic_cast(Parent::function->getScopeUnit(entry.first.scope)); assert(self == this); - + self->__declarationsOverriden.emplace(entry.first.identifier, entry.second); } } - void registerChildScope(std::shared_ptr scope){ + void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __rawVars.clear(); __declarationsOverriden.clear(); __childScopes.clear(); } private: std::unordered_map __declarationsOverriden; std::unordered_map __rawVars; - std::list> __childScopes; + std::list> __childScopes; }; /** \brief Provides automatic type conversion - * \extends xreate::compilation::ICodeScopeUnit + * \extends xreate::compilation::IBruteScope */ template class TypeConversionScopeDecorator: public Parent { public: - TypeConversionScopeDecorator(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} - - llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="") override { - llvm::Value* resultR = Parent::process(expr, hintVarDecl); - + TypeConversionScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} + + llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation()) override { + llvm::Value* resultR = Parent::process(expr, hintVarDecl, expectedT); + if(!expr.type.isValid()) { - return resultR; + return resultR; } ExpandedType exprT = Parent::pass->man->root->getType(expr); llvm::Type* exprTR = Parent::pass->man->llvm->toLLVMType(exprT); - return typeinference::doAutomaticTypeConversion(resultR, exprTR, Parent::pass->man->llvm->builder); + return typeinference::doAutomaticTypeConversion(resultR, exprTR, Parent::pass->man->llvm->irBuilder); } }; +#ifndef XREATE_CONFIG_MIN /**\brief The default code scope compilation implementation - * \extends xreate::compilation::ICodeScopeUnit + * \extends xreate::compilation::IBruteScope */ -typedef CachedScopeDecorator< - TypeConversionScopeDecorator< - latex::LatexBruteScopeDecorator< - polymorph::PolymorphCodeScopeUnit< - compilation::TransformationsScopeDecorator< - interpretation::InterpretationScopeDecorator< - versions::VersionsScopeDecorator>>>>>> - - DefaultCodeScopeUnit; - -} //end of compilation namespace - -struct CachedScopeDecoratorTag; -struct VersionsScopeDecoratorTag; - -template<> -struct DecoratorsDict{ - typedef compilation::CachedScopeDecorator< - compilation::TypeConversionScopeDecorator< - latex::LatexBruteScopeDecorator< - polymorph::PolymorphCodeScopeUnit< - compilation::TransformationsScopeDecorator< +typedef + CachedScopeDecorator< + TypeConversionScopeDecorator< + latex::LatexBruteScopeDecorator< + polymorph::PolymorphBruteScopeDecorator< + compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< - versions::VersionsScopeDecorator>>>>>> - - result; -}; - -template<> -struct DecoratorsDict{ - typedef versions::VersionsScopeDecorator< - compilation::BasicCodeScopeUnit> result; -}; - + versions::VersionsScopeDecorator< + compilation::BasicBruteScope + >>>>>>> + DefaultCodeScopeUnit; + + } //end of compilation namespace + + struct CachedScopeDecoratorTag; + struct VersionsScopeDecoratorTag; + + template<> + struct DecoratorsDict{ + typedef compilation::CachedScopeDecorator< + compilation::TypeConversionScopeDecorator< + latex::LatexBruteScopeDecorator< + polymorph::PolymorphBruteScopeDecorator< + compilation::TransformationsScopeDecorator< + interpretation::InterpretationScopeDecorator< + versions::VersionsScopeDecorator< + compilation::BasicBruteScope + >>>>>>> + + result; + }; + + template<> + struct DecoratorsDict{ + typedef + versions::VersionsScopeDecorator< + compilation::BasicBruteScope + > + result; + }; + +#else + /**\brief The default code scope compilation implementation + * \extends xreate::compilation::IBruteScope + */ + typedef + CachedScopeDecorator< + TypeConversionScopeDecorator< + interpretation::InterpretationScopeDecorator< + demand::DemandBruteScopeDecorator< + polymorph::PolymorphBruteScopeDecorator< + compilation::BasicBruteScope + >>>>> + DefaultCodeScopeUnit; + + } //end of compilation namespacef + + struct CachedScopeDecoratorTag; + + template<> + struct DecoratorsDict{ + typedef compilation::CachedScopeDecorator< + compilation::TypeConversionScopeDecorator< + interpretation::InterpretationScopeDecorator< + demand::DemandBruteScopeDecorator< + polymorph::PolymorphBruteScopeDecorator< + compilation::BasicBruteScope + >>>>> + result; + }; + + typedef + demand::DemandBruteFnDecorator< + //polymorph::PolymorphBruteFnDecorator< + compilation::BasicBruteFunction + > BruteFunctionDefault; +#endif } //end of xreate #endif /* SCOPEDECORATORS_H */ diff --git a/cpp/src/compilation/demand.cpp b/cpp/src/compilation/demand.cpp new file mode 100644 index 0000000..4edba65 --- /dev/null +++ b/cpp/src/compilation/demand.cpp @@ -0,0 +1,22 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: pgess + * Created on June 23, 2018, 4:33 PM + */ + +/** + * \file src/compilation/latex.h + * \brief Latex(Late Context) support + */ + +#include "compilation/demand.h" +#include "llvmlayer.h" + +namespace xreate{ +namespace demand{ + +} +} \ No newline at end of file diff --git a/cpp/src/compilation/demand.h b/cpp/src/compilation/demand.h new file mode 100644 index 0000000..9a9e64b --- /dev/null +++ b/cpp/src/compilation/demand.h @@ -0,0 +1,101 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +/* + * Author: pgess + * Created on June 23, 2018, 2:51 PM + * + */ + +#ifndef LATEX_H +#define LATEX_H + +#include "query/demand.h" +#include "analysis/utils.h" +#include "pass/compilepass.h" + +namespace xreate{namespace demand{ + +template +class DemandBruteFnDecorator : public Parent{ +public: + DemandBruteFnDecorator(ManagedFnPtr f, CompilePass *p): Parent(f, p){} + +protected: + std::vector + prepareSignature() override{ + std::vector &&signature = Parent::prepareSignature(); + + DemandQuery* query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::DemandQuery)); + const Demand& demand = query->getFnDemand(Parent::function->getName()); + signature.reserve(signature.size() + demand.size()); + + unsigned int argOffset = signature.size(); + for(const auto &rec: demand){ + TypeAnnotation argTCust(TypeOperator::ALIAS, {}); + argTCust.__valueCustom = rec.second; + const ExpandedType &argT = Parent::pass->man->root->expandType(argTCust); + Expression bindingE; + bindingE.type = argT; + llvm::Type *argTRaw = Parent::pass->man->llvm->toLLVMType(argT); + + Parent::function->addBinding( + Atom(std::string(rec.first)), + std::move(bindingE), + argOffset++ + ); + signature.push_back(argTRaw); + } + + return signature; + } +}; + +/** + * \brief Latex aware \ref xreate::compilation::IBruteScope decorator + * \implements xreate::compilation::IBruteScope + */ +template +class DemandBruteScopeDecorator : public Parent{ +public: + DemandBruteScopeDecorator(const CodeScope *const codeScope, compilation::IBruteFunction *f, CompilePass *compilePass) + : Parent(codeScope, f, compilePass){} + + compilation::IFnInvocation * + findFunction(const Expression& opCall){ + DemandQuery* query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::DemandQuery)); + const std::string &calleeName = opCall.getValueString(); + compilation::IFnInvocation *opInvocBase = Parent::findFunction(opCall); + + const Demand &demand = query->getFnDemand(calleeName); + if(!demand.size()) return opInvocBase; + + //prepare additional arguments + std::vector argsDemand; + argsDemand.reserve(demand.size()); + + const Supply& argsActual = query->getFnSupply(ASTSite{opCall.id}); + for(const auto& arg: demand){ + TypeAnnotation argTCust(TypeOperator::ALIAS, {}); + argTCust.__valueCustom = arg.second; + const ExpandedType &argScheme = Parent::pass->man->root->expandType(argTCust); + //llvm::Type* argSchemeRaw = Parent::pass->man->llvm->toLLVMType(argScheme); + const Gringo::Symbol argTrAtom = argsActual.at(arg.first); + Expression argE = analysis::representTransExpression(argTrAtom, argScheme, Parent::pass->man->transcend); + argE.type = argScheme; + llvm::Value* argValueRaw = Parent::process(argE); + + argsDemand.push_back(argValueRaw); + } + + return new compilation::HiddenArgsFnInvocation(std::move(argsDemand), opInvocBase); + } +}; + +}} //end of namespace xreate::demand + +#endif /* LATEX_H */ + diff --git a/cpp/src/compilation/interpretation-instructions.cpp b/cpp/src/compilation/i12ninst.cpp similarity index 97% rename from cpp/src/compilation/interpretation-instructions.cpp rename to cpp/src/compilation/i12ninst.cpp index f4387bd..72d320d 100644 --- a/cpp/src/compilation/interpretation-instructions.cpp +++ b/cpp/src/compilation/i12ninst.cpp @@ -1,113 +1,113 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * Created on June 15, 2018, 5:32 PM */ -#include "compilation/interpretation-instructions.h" +#include "compilation/i12ninst.h" #include "analysis/interpretation.h" #include "transcendlayer.h" #include "targets.h" #include "aux/latereasoning.h" #include "latetranscend.h" #include "aux/transcend-decorators.h" #include "compilation/targetinterpretation.h" using namespace std; using namespace xreate::latereasoning; namespace xreate{ namespace interpretation{ Expression IntrinsicQueryInstruction::process(const Expression& expression) { AST* ast = static_cast (__fnI12n->man)->ast; TranscendLayer* transcend = static_cast (__fnI12n->man)->pass->man->transcend; ExpandedType targetT = ast->getType(expression); assert(expression.operands.size() == 1); assert(expression.operands.front().__state == Expression::STRING); assert(targetT->__operator == TypeOperator::LIST_ARRAY); std::string namePredicate = expression.operands.front().getValueString(); StaticModel model = (static_cast (__fnI12n->man))->pass->man->transcend->query(namePredicate); Expression result(Operator::LIST,{}); result.operands.reserve(model.size()); ExpandedType elementT = targetT->__operands.at(0).__operator == TypeOperator::SLAVE ? dereferenceSlaveType(ExpandedType(targetT->__operands.at(0)), transcend) : ExpandedType(targetT->__operands.at(0)); if(model.size()) { if (elementT->__operator == TypeOperator::LIST_RECORD) { //edge case, content's type is LIST_NAMED: for(const auto& row : model) { result.operands.push_back(representTransExpression(row.second, elementT, transcend)); } } else { for (const auto& row : model) { assert(row.second.args().size); result.operands.push_back(representTransExpression(row.second.args()[0], elementT, transcend)); } } } return result; } llvm::Value* IntrinsicQueryInstruction::processLate(const Expression& expression, const compilation::Context& context) { assert(expression.blocks.size()); CodeScope* body = expression.blocks.front(); PassManager* man = static_cast (__fnI12n->man)->pass->man; compilation::ICodeScopeUnit* bodyBrute = context.function->getScopeUnit(body); llvm::Type* instructionT = man->llvm->toLLVMType(man->root->getType(expression)); Expression atomNameE = __fnI12n->getScope(context.scope->scope)->process(expression.operands.front()); assert(atomNameE.__state == Expression::STRING); string atomName = atomNameE.getValueString(); std::string argName = expression.bindings.front(); ExpandedType argT = man->root->getType(body->getDefinition(body->getSymbol(argName))); if (argT->__operator == TypeOperator::SLAVE){ argT = dereferenceSlaveType(argT, man->transcend); } auto transcend = Decorators::getInterface(man->transcend); LateReasoningCompiler* compiler = new latereasoning::LateReasoningCompiler(__fnI12n, context); - SymbolPacked targetSP = transcend->pack(Attachments::get(expression)); + SymbolPacked targetSP = transcend->pack(Attachments::get(expression)); LateAnnotationsGroup feedbackAG = transcend->queryLate(atomName); for(const auto& feedback : feedbackAG.annotations) { SymbolPacked targetExpectedSP = ParseImplAtom::get(feedback.first); if (targetExpectedSP == targetSP) { const LateAnnotation& annotation = feedback.second; return compiler->compileAutoExpand(annotation, instructionT, "", [transcend, &argT, this, body, bodyBrute, &argName](const Gringo::Symbol & atomRaw) { InterpretationScope* bodyI12n = __fnI12n->getScope(body); Expression argValue; if (argT->__operator == TypeOperator::LIST_RECORD) { argValue = representTransExpression(atomRaw, argT, transcend); } else { argValue = representTransExpression(atomRaw.args()[0], argT, transcend); } bodyI12n->overrideBindings({ {argValue, argName}}); bodyBrute->reset(); return bodyBrute->compile(); }); } } assert(false && "No appropriate late annotation"); return nullptr; //should not be ever reachable } } } //end of xreate::interpretation diff --git a/cpp/src/compilation/interpretation-instructions.h b/cpp/src/compilation/i12ninst.h similarity index 100% rename from cpp/src/compilation/interpretation-instructions.h rename to cpp/src/compilation/i12ninst.h diff --git a/cpp/src/compilation/intrinsics.cpp b/cpp/src/compilation/intrinsics.cpp new file mode 100644 index 0000000..c8582ba --- /dev/null +++ b/cpp/src/compilation/intrinsics.cpp @@ -0,0 +1,58 @@ +//March 2020 + +#include "compilation/intrinsics.h" +#include "analysis/utils.h" +#include "llvmlayer.h" + +using namespace std; + +namespace xreate{ namespace compilation{ + +llvm::Value* +IntrinsicCompiler::compile(const Expression& e, const Context& context, const std::string& hintAlias){ + switch ((IntrinsicFn) e.getValueDouble()){ + case IntrinsicFn::ARR_INIT:{ + return nullptr; +// const ExpandedType& typAggr = context.pass->man->root->getType(e); +// llvm::Type* typAggrRaw = context.pass->man->llvm->toLLVMType(typAggr); +// llvm::Value* lengthRaw = context.scope->process(e.operands.at(0)); +// +// ContainerInst engine(context); +// return engine.array_init(lengthRaw, typAggrRaw); + } + + default: break; + } + + assert(false); + return nullptr; +} + +Expression +IntrinsicCompiler::interpret(const Expression& e){ + switch ((IntrinsicFn) e.getValueDouble()){ + case IntrinsicFn::REC_FIELDS: { + return rec_fields(e); + } + default: break; + } + + assert(false); + return Expression(); +} + +Expression +IntrinsicCompiler::rec_fields(const Expression& e){ + TypesHelper helper(__man->llvm); + assert(e.operands.size() == 1); + + const Expression argTypeE = e.operands.at(0); + assert(argTypeE.__state == Expression::STRING); + const string& argTypeS = argTypeE.getValueString(); + const ExpandedType argTypeT = __man->root->findType(argTypeS); + + const auto& fields = helper.getRecordFields(argTypeT); + return analysis::representVecStr(fields); +} + +}} \ No newline at end of file diff --git a/cpp/src/compilation/intrinsics.h b/cpp/src/compilation/intrinsics.h new file mode 100644 index 0000000..98f3c42 --- /dev/null +++ b/cpp/src/compilation/intrinsics.h @@ -0,0 +1,26 @@ +//March 2020 + +#ifndef XREATE_INTRINSICS_H +#define XREATE_INTRINSICS_H + +#include "ast.h" +#include "pass/compilepass.h" +namespace llvm { + class Value; +} + +namespace xreate{ namespace compilation{ +class IntrinsicCompiler{ +public: + IntrinsicCompiler(PassManager* manager): __man(manager){}; + + llvm::Value* compile(const Expression& e, const Context& context, const std::string& hintAlias); + Expression interpret(const Expression& e); + +private: + PassManager* __man; + + Expression rec_fields(const Expression& e); +}; +}} +#endif //XREATE_INTRINSICS_H diff --git a/cpp/src/compilation/latetranscend.cpp b/cpp/src/compilation/latetranscend.cpp index 54ce3e7..0c584ed 100644 --- a/cpp/src/compilation/latetranscend.cpp +++ b/cpp/src/compilation/latetranscend.cpp @@ -1,183 +1,183 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * latereasoning.cpp * * Author: pgess * Created on May 26, 2018, 3:54 PM */ /** * \class xreate::latereasoning::LateReasoningCompiler * Provides Late Transcend feature. See [Late Transcend](/d/transcend/late-transcend/) for the general overview. */ #include "compilation/latetranscend.h" //#include "aux/latereasoning.h" -#include "compilation/scopedecorators.h" +#include "compilation/decorators.h" #include "analysis/interpretation.h" #include "compilation/targetinterpretation.h" #include using namespace xreate::interpretation; using namespace xreate::compilation; using namespace std; namespace xreate{ namespace latereasoning{ #define HINT(x) (hint.empty()? x : hint) std::map LateReasoningCompiler:: __dictGuardDefinitions = std::map(); llvm::Value* LateReasoningCompiler::processSwitchLateStatement(const Expression& expr, const std::string& hint) { AST* root = __context.pass->man->root; LLVMLayer* llvm = __context.pass->man->llvm; CodeScope* scopeBody = expr.blocks.front(); Symbol guardS = Symbol{scopeBody->getSymbol(expr.bindings.front()), scopeBody}; const ExpandedType& guardT = root->getType(expr.operands.at(0)); const ExpandedType& guardTPlain = guardT->__operator == TypeOperator::SLAVE? interpretation::dereferenceSlaveType(guardT, __context.pass->man->transcend) : guardT; llvm::Value * guardRaw = __context.scope->process(expr.operands.at(0)); llvm::Type* instructionT = llvm->toLLVMType(root->getType(expr)); return compileExpand(guardS, guardRaw, guardTPlain, instructionT, hint, [this, scopeBody]() { ICodeScopeUnit* bodyUnit = this->__context.function->getScopeUnit(scopeBody); bodyUnit->reset(); return this->__context.function->getScopeUnit(scopeBody)->compile(); }); } llvm::Value* LateReasoningCompiler::compileAutoExpand(const LateAnnotation& annotation, llvm::Type* resultT, const std::string& hint, Handler handler) { TranscendLayer* transcend = __context.pass->man->transcend; AST* root = __context.pass->man->root; const std::list& guardKeys = annotation.guardKeys; std::list guardsToExpand; for(const SymbolPacked key : guardKeys) { if(!__dictGuardDefinitions.count(key)) { const Symbol& keyS = transcend->unpack(key); InterpretationScope* keyScope = __fnI12n->getScope(keyS.scope); if (!keyScope->isBindingDefined(keyS.identifier)) { guardsToExpand.push_back(keyS); } } } typedef std::function < llvm::Value * () > Compiler; Compiler programInit([handler, annotation, this]() { std::list&& values = findKeys(annotation.guardKeys); auto answer = annotation.select(values, __context.pass->man->root, __context.pass->man->transcend); assert(answer); return handler(*answer); }); Compiler aggregate = std::accumulate(guardsToExpand.begin(), guardsToExpand.end(), programInit, [this, root, transcend, &resultT, hint](Compiler program, const Symbol & key) { const ExpandedType& keyT = root->getType(CodeScope::getDefinition(key)); const ExpandedType& keyTPlain = keyT->__operator == TypeOperator::SLAVE? interpretation::dereferenceSlaveType(keyT, transcend) : keyT; return Compiler([this, key, keyTPlain, resultT, hint, program](){ llvm::Value * keyRaw = __context.scope->processSymbol(key); return compileExpand(key, keyRaw, keyTPlain, resultT, hint, program); }); } ); return aggregate(); } llvm::Value* LateReasoningCompiler::compileExpand(const Symbol& keyS, llvm::Value* keyRaw, const ExpandedType& domainT, llvm::Type* resultT, const std::string& hint, CompilerHandler compilerBody) { assert(domainT->__operator == TypeOperator::VARIANT); std::list domInstancesList = generateAllInstancesInDomain2(domainT); std::vector domInstances(domInstancesList.begin(), domInstancesList.end()); const int countInstances = domInstances.size(); assert(countInstances); TranscendLayer* transcend = __context.pass->man->transcend; SymbolPacked keyP = transcend->pack(keyS); LLVMLayer* llvm = __context.pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; compilation::IFunctionUnit* function = __context.function; llvm::Type* typI8 = llvm::Type::getInt8Ty(llvm->llvmContext); llvm::Value* keyVariantRaw = builder.CreateExtractValue(keyRaw, llvm::ArrayRef({0})); llvm::SwitchInst* instructionSwitch = builder.CreateSwitch(keyVariantRaw, nullptr, countInstances); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create( llvm->llvmContext, "epilog", function->raw); builder.SetInsertPoint(blockEpilog); llvm::PHINode *ret = builder.CreatePHI(resultT, countInstances, HINT("reverse")); llvm::BasicBlock* blockDefault = nullptr; for (int instanceId = 0; instanceId < countInstances; ++instanceId) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create( llvm->llvmContext, "case" + std::to_string(instanceId), function->raw); if(instanceId == 0) blockDefault = blockCase; builder.SetInsertPoint(blockCase); //assign guard values const Expression& instanceE = domInstances.at(instanceId); __dictGuardDefinitions[keyP] = instanceE; // __fnI12n->getScope(keyS.scope)->overrideBindings({ // {instanceE, keyS.identifier} // }); //invoke further compilation handler llvm::Value* resultCase = compilerBody(); ret->addIncoming(resultCase, builder.GetInsertBlock()); instructionSwitch->addCase(llvm::dyn_cast(llvm::ConstantInt::get(typI8, instanceId)), blockCase); builder.CreateBr(blockEpilog); } //erase guard assignment __dictGuardDefinitions.erase(keyP); instructionSwitch->setDefaultDest(blockDefault); builder.SetInsertPoint(blockEpilog); return ret; } std::list LateReasoningCompiler::findKeys(const std::list& keys) { TranscendLayer* transcend = __context.pass->man->transcend; std::list result; InterpretationScope* scopeI12n = __fnI12n->getScope(__context.scope->scope); std::transform(keys.begin(), keys.end(), std::inserter(result, result.end()), [this, scopeI12n, transcend](const SymbolPacked & key) { if (__dictGuardDefinitions.count(key)){ return __dictGuardDefinitions.at(key); } return scopeI12n->processSymbol(transcend->unpack(key)); }); return result; } } } diff --git a/cpp/src/compilation/latex.cpp b/cpp/src/compilation/latex.cpp deleted file mode 100644 index 9a4d11e..0000000 --- a/cpp/src/compilation/latex.cpp +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * Author: pgess - * Created on June 23, 2018, 4:33 PM - */ - -/** - * \file src/compilation/latex.h - * \brief Latex(Late Context) support - */ - -#include "compilation/latex.h" -#include "analysis/interpretation.h" -#include "llvmlayer.h" - -namespace xreate{ -namespace latex{ - -ExpandedType -getSubjectDomain(const std::string& subject, LatexQuery* query){ - std::list&& column = query->getSubjectDomain(subject); - return ExpandedType(interpretation::collapseColumn(column)); -} - -llvm::Value* -ExtraArgsFnInvocation::operator() (std::vector&& args, const std::string& hintDecl) { - args.insert(args.end(), __argsLatex.begin(), __argsLatex.end()); - - return __parent->operator ()(std::move(args), hintDecl); -} - -} -} \ No newline at end of file diff --git a/cpp/src/compilation/latex.h b/cpp/src/compilation/latex.h deleted file mode 100644 index cbb7529..0000000 --- a/cpp/src/compilation/latex.h +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - */ - -/* - * Author: pgess - * Created on June 23, 2018, 2:51 PM - * - * \file latex.h - * \brief latex - */ - -#ifndef LATEX_H -#define LATEX_H - -#include "compilation/latetranscend.h" -#include "compilation/interpretation-instructions.h" -#include "query/latex.h" -#include "pass/compilepass.h" -#include "analysis/interpretation.h" -#include "compilation/targetinterpretation.h" - -namespace xreate{ -namespace latex{ - -ExpandedType -getSubjectDomain(const std::string &subject, LatexQuery *query); - -/** \brief Latex(Late Context)-aware decorator for \ref xreate::compilation::IFunctionUnit - * \extends xreate::compilation::IFunctionUnit - */ -template -class LatexBruteFunctionDecorator : public Parent{ -public: - - LatexBruteFunctionDecorator(ManagedFnPtr f, CompilePass *p) - : Parent(f, p){ - __query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::LatexQuery)); - } - -protected: - std::vector - prepareSignature(){ - std::vector &&signature = Parent::prepareSignature(); - - const Demand &demand = __query->getFnDemand(Parent::function->getName()); - signature.reserve(signature.size() + demand.size()); - - int subjectId = __query->LatexParametersOffset; - for(const std::string &subject: demand){ - const ExpandedType &subjectT = getSubjectDomain(subject, __query); - Expression bindingE; - bindingE.type = subjectT; - std::string argCaption = std::string("latex_") + subject; - Parent::function->addBinding( - Atom(std::string(argCaption)), - std::move(bindingE), - subjectId++); - - llvm::Type *subjectTRaw = Parent::pass->man->llvm->toLLVMType(subjectT); - signature.push_back(subjectTRaw); - } - - return signature; - } - -public: - LatexQuery *__query; -}; - -/** \brief %Function invocation operator decorator to handle latex enabled functions with hidden extra arguments */ -class ExtraArgsFnInvocation : public compilation::IFnInvocation{ -public: - - ExtraArgsFnInvocation(std::vector argsLatex, compilation::IFnInvocation *parent) - : __argsLatex(argsLatex), __parent(parent){} - - llvm::Value *operator()(std::vector &&args, const std::string &hintDecl = ""); - -private: - std::vector __argsLatex; - compilation::IFnInvocation *__parent; -}; - -/** - * \brief Latex aware \ref xreate::compilation::ICodeScopeUnit decorator - * \implements xreate::compilation::ICodeScopeUnit - */ -template -class LatexBruteScopeDecorator : public Parent{ -public: - - LatexBruteScopeDecorator(const CodeScope *const codeScope, compilation::IFunctionUnit *f, CompilePass *compilePass) - : Parent(codeScope, f, compilePass){} - - compilation::IFnInvocation * - findFunction(const Expression &opCall){ - compilation::IFnInvocation *invocDefault = Parent::findFunction(opCall); - const std::string &calleeName = opCall.getValueString(); - LatexQuery *query = reinterpret_cast (Parent::pass->man->transcend->getQuery(QueryId::LatexQuery)); - - const Demand &fnCalleeDemand = query->getFnDemand(calleeName); - if(!fnCalleeDemand.size()) return invocDefault; - - //prepare latex arguments - std::vector argsLatex; - argsLatex.reserve(fnCalleeDemand.size()); - - for(const std::string &subject: fnCalleeDemand){ - ExpandedType subjectT = getSubjectDomain(subject, query); - llvm::Type *subjectTRaw = Parent::pass->man->llvm->toLLVMType(subjectT); - const latereasoning::LateAnnotation &decision = query->getDecision(subject, Parent::scope); - - compilation::Context ctx{this, Parent::function, Parent::pass}; - interpretation::InterpretationScope *scopeIntrpr = - Parent::pass->targetInterpretation->transformContext(ctx); - latereasoning::LateReasoningCompiler *compiler - = new latereasoning::LateReasoningCompiler( - dynamic_cast(scopeIntrpr->function), ctx); - - llvm::Value *subjectRaw = compiler->compileAutoExpand( - decision, - subjectTRaw, - subject, - [&](const Gringo::Symbol &decisionRaw){ - const Expression &decisionE = interpretation::representTransExpression( - decisionRaw.args()[2], subjectT, Parent::pass->man->transcend); - Attachments::put(decisionE, subjectT); - return Parent::process(decisionE, subject); - }); - - argsLatex.push_back(subjectRaw); - } - - return new ExtraArgsFnInvocation(std::move(argsLatex), invocDefault); - } -}; - -} -} //end of namespace xreate::context - -#endif /* LATEX_H */ - diff --git a/cpp/src/compilation/operators.cpp b/cpp/src/compilation/operators.cpp deleted file mode 100644 index 087b766..0000000 --- a/cpp/src/compilation/operators.cpp +++ /dev/null @@ -1,71 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. - * - * operators.cpp - * - * Author: pgess - * Created on April 8, 2017, 1:35 PM - */ - -#include "operators.h" -#include "llvmlayer.h" -#include "ExternLayer.h" -#include - -using namespace llvm; -using namespace std; - -namespace xreate { - -namespace pointerarithmetic { - -llvm::Value* -PointerArithmetic::add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl){ - LLVMLayer* llvm = context.pass->man->llvm; - - if (left->getType()->isPointerTy() && right->getType()->isIntegerTy()){ - - std::vector indexes{right}; - //{llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0)}; - //indexes.push_back(right); - - return llvm->builder.CreateGEP(left, llvm::ArrayRef(indexes), hintVarDecl); - } - - return nullptr; -} -}//end of pointgerarithmetic namespace - -llvm::Value* -StructUpdate::add(const Expression& left, llvm::Value *leftRaw, const Expression& right, compilation::Context context, const std::string& hintVarDecl){\ - if (!(right.__state == Expression::COMPOUND && right.op == Operator::LIST)){ - return nullptr; - } - - PassManager* man = context.pass->man; - - ExpandedType tyOperandLeft = man->root->getType(left); - - const std::vector fieldsFormal = (tyOperandLeft.get().__operator == TypeOperator::CUSTOM)? - man->llvm->layerExtern->getStructFields(man->llvm->layerExtern->lookupType(tyOperandLeft.get().__valueCustom)) - : tyOperandLeft.get().fields; - - std::map indexFields; - for(size_t i=0, size = fieldsFormal.size(); iprocess(value, right.bindings.at(i)); - unsigned int fieldId = indexFields.at(right.bindings.at(i)); - - result = man->llvm->builder.CreateInsertValue(result, valueRaw, llvm::ArrayRef({fieldId})); - } - - return result; -} - -} //end of xreate namespace diff --git a/cpp/src/compilation/pointers.cpp b/cpp/src/compilation/pointers.cpp new file mode 100644 index 0000000..5b70a05 --- /dev/null +++ b/cpp/src/compilation/pointers.cpp @@ -0,0 +1,44 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * operators.cpp + * + * Author: pgess + * Created on April 8, 2017, 1:35 PM + */ + +#include "pointers.h" +#include "llvmlayer.h" + +#ifdef XREATE_ENABLE_EXTERN + #include "ExternLayer.h" +#endif + +#include + +using namespace llvm; +using namespace std; + +namespace xreate { + +namespace pointers { + +llvm::Value* +PointerArithmetic::add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl){ + LLVMLayer* llvm = context.pass->man->llvm; + + if (left->getType()->isPointerTy() && right->getType()->isIntegerTy()){ + + std::vector indexes{right}; + //{llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0)}; + //indexes.push_back(right); + + return llvm->irBuilder.CreateGEP(left, llvm::ArrayRef(indexes), hintVarDecl); + } + + return nullptr; +} + +}//end of pointers namespace +} //end of xreate namespace diff --git a/cpp/src/compilation/operators.h b/cpp/src/compilation/pointers.h similarity index 69% rename from cpp/src/compilation/operators.h rename to cpp/src/compilation/pointers.h index 51a0448..1919bfb 100644 --- a/cpp/src/compilation/operators.h +++ b/cpp/src/compilation/pointers.h @@ -1,46 +1,38 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: operators.h * Author: pgess * * Created on April 8, 2017, 1:33 PM */ /** * \file operators.h * \brief Helpers to compile various operators */ #ifndef OPERATORS_H #define OPERATORS_H #include "pass/compilepass.h" namespace llvm{ class Value; } -namespace xreate{ namespace pointerarithmetic{ +namespace xreate{ namespace pointers{ /** \brief Pointer arithmetic operators */ class PointerArithmetic{ public: /** \brief Pointer arithmetic operator */ static llvm::Value* add(llvm::Value *left, llvm::Value *right, compilation::Context context, const std::string& hintVarDecl); }; -} //end of pointerarithmetic namespace - -/** \brief Struct update operators */ -class StructUpdate{ -public: - - /** \brief Struct update operator */ - static llvm::Value* add(const Expression& left, llvm::Value *leftRaw, const Expression& right, compilation::Context context, const std::string& hintVarDecl); -} ; +} //end of pointers namespace } //namespace xreate #endif /* OPERATORS_H */ diff --git a/cpp/src/compilation/polymorph.cpp b/cpp/src/compilation/polymorph.cpp index 97e654b..900c71c 100644 --- a/cpp/src/compilation/polymorph.cpp +++ b/cpp/src/compilation/polymorph.cpp @@ -1,50 +1,68 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on July 9, 2018, 6:04 PM */ #include "compilation/polymorph.h" using namespace std; namespace xreate{ namespace polymorph{ -PolymorphFnInvocation::PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, - std::list calleeSpecializations, - CompilePass* pass, - PolymorphQuery* query, - LLVMLayer* llvm, - latereasoning::LateReasoningCompiler* compiler) -: __selector(selector), __calleeSpecializations(calleeSpecializations), -__pass(pass), __query(query), __llvm(llvm), __compiler(compiler) { } - -llvm::Value* -PolymorphFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { - std::map dictSelectors; - for(ManagedFnPtr specialization : __calleeSpecializations) { - dictSelectors.emplace(specialization->guard, specialization); +//PolymorphFnInvocation::PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, +// std::list calleeSpecializations, +// CompilePass* pass, +// PolymorphQuery* query, +// LLVMLayer* llvm, +// latereasoning::LateReasoningCompiler* compiler) +//: __selector(selector), __calleeSpecializations(calleeSpecializations), +//__pass(pass), __query(query), __llvm(llvm), __compiler(compiler) { } +// +//llvm::Value* +//PolymorphFnInvocation::operator()(std::vector&& args, const std::string& hintDecl) { +// std::map dictSelectors; +// for(ManagedFnPtr specialization : __calleeSpecializations) { +// dictSelectors.emplace(specialization->guard, specialization); +// } +// +// compilation::IBruteFunction* specAny = __pass->getFunctionUnit(__calleeSpecializations.front()); +// return __compiler->compileAutoExpand( +// __selector, +// specAny->prepareResult(), +// hintDecl, +// [this, &args, hintDecl, &dictSelectors](const Gringo::Symbol & selectorRaw) { +// const Expression & selectorE = __query->getValue(selectorRaw); +// assert(dictSelectors.count(selectorE) && "Inappropriate specialization guard"); +// compilation::BruteFnInvocation* invoc = new compilation::BruteFnInvocation( +// __pass->getFunctionUnit(dictSelectors.at(selectorE))->compile(), +// __llvm); +// +// return invoc->operator()(move(args), hintDecl); +// }); +//} + +bool isFnGuarded(const std::string& fn, const AST& root){ + const std::list& specs = root.getFnSpecializations(fn); + + //Extern function + if(specs.size() == 0){ + return false; + } + + //No other specializations. Check if it has no guard + if(specs.size() == 1){ + if(!specs.front()->guard.isValid()){ + return false; } + } - compilation::IFunctionUnit* specAny = __pass->getFunctionUnit(__calleeSpecializations.front()); - return __compiler->compileAutoExpand( - __selector, - specAny->prepareResult(), - hintDecl, - [this, &args, hintDecl, &dictSelectors](const Gringo::Symbol & selectorRaw) { - const Expression & selectorE = __query->getValue(selectorRaw); - assert(dictSelectors.count(selectorE) && "Inappropriate specialization guard"); - compilation::BruteFnInvocation* invoc = new compilation::BruteFnInvocation( - __pass->getFunctionUnit(dictSelectors.at(selectorE))->compile(), - __llvm); - - return invoc->operator()(move(args), hintDecl); - }); -} -} + return true; } + +}} diff --git a/cpp/src/compilation/polymorph.h b/cpp/src/compilation/polymorph.h index 71194d6..5180719 100644 --- a/cpp/src/compilation/polymorph.h +++ b/cpp/src/compilation/polymorph.h @@ -1,103 +1,161 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * Created on October 7, 2017 */ /** * \file src/compilation/polymorph.h * \brief Polymorphism-aware compilation routines */ #ifndef POLYMORPHCOMPILER_H #define POLYMORPHCOMPILER_H #include "pass/compilepass.h" #include "query/polymorph.h" #include "compilation/latetranscend.h" #include "compilation/targetinterpretation.h" +#include "analysis/utils.h" +#include "aux/expressions.h" namespace xreate{ namespace polymorph{ -typedef Expression Selector; +//typedef Expression Selector; /** \brief An instance of \ref compilation::IFnInvocation to manage polymorphic functions invocation*/ -class PolymorphFnInvocation: public compilation::IFnInvocation{ -public: - PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, - std::list calleeSpecializations, - CompilePass* pass, - PolymorphQuery* query, - LLVMLayer* llvm, - latereasoning::LateReasoningCompiler* compiler); - - llvm::Value* operator()(std::vector&& args, const std::string& hintDecl = ""); - -private: - latereasoning::LateAnnotation __selector; - std::list __calleeSpecializations; - - CompilePass* __pass; - PolymorphQuery* __query; - LLVMLayer* __llvm; - latereasoning::LateReasoningCompiler* __compiler; -} ; +//class PolymorphFnInvocation: public compilation::IFnInvocation{ +//public: +// PolymorphFnInvocation(const latereasoning::LateAnnotation& selector, +// std::list calleeSpecializations, +// CompilePass* pass, +// PolymorphQuery* query, +// LLVMLayer* llvm, +// latereasoning::LateReasoningCompiler* compiler); +// +// llvm::Value* operator()(std::vector&& args, const std::string& hintDecl = ""); +// +//private: +// latereasoning::LateAnnotation __selector; +// std::list __calleeSpecializations; +// +// CompilePass* __pass; +// PolymorphQuery* __query; +// LLVMLayer* __llvm; +// latereasoning::LateReasoningCompiler* __compiler; +//} ; /** - * \brief Polymorphism aware \ref xreate::compilation::ICodeScopeUnit decorator - * \implements xreate::compilation::ICodeScopeUnit + * \brief Polymorphism aware \ref xreate::compilation::IBruteScope decorator + * \implements xreate::compilation::IBruteScope */ + +bool isFnGuarded(const std::string& fn, const AST& root); + template -class PolymorphCodeScopeUnit: public Parent{ +class PolymorphBruteScopeDecorator: public Parent{ public: - PolymorphCodeScopeUnit(const CodeScope * const codeScope, - compilation::IFunctionUnit* f, - CompilePass* compilePass) + PolymorphBruteScopeDecorator(const CodeScope * const codeScope, + compilation::IBruteFunction* f, + CompilePass* compilePass) : Parent(codeScope, f, compilePass){ } protected: compilation::IFnInvocation* findFunction(const Expression& opCall) override{ - // //Check does invocation require guards satisfaction - const std::string& nameCallee = opCall.getValueString(); - const std::list& specializations = - Parent::pass->man->root->getFunctionSpecializations(nameCallee); + LLVMLayer* llvm = Parent::pass->man->llvm; + AST* ast = Parent::pass->man->root; + TranscendLayer* transcend = Parent::pass->man->transcend; - //Extern function - if(specializations.size() == 0){ + //Check does invocation require guards satisfaction + const std::string& calleeName = opCall.getValueString(); + if(!isFnGuarded(calleeName, *ast)){ return Parent::findFunction(opCall); } - //No other specializations. Check if it has no guard - if(specializations.size() == 1){ - if(!specializations.front()->guard.isValid()){ - return Parent::findFunction(opCall); - } - } - //Several specializations PolymorphQuery* query = dynamic_cast ( Parent::pass->man->transcend->getQuery(QueryId::PolymorphQuery)); - const latereasoning::LateAnnotation& selector = query->get(opCall); - compilation::Context ctx{this, Parent::function, Parent::pass}; - interpretation::InterpretationScope* scopeIntrpr = - Parent::pass->targetInterpretation->transformContext(ctx); - latereasoning::LateReasoningCompiler* compiler - = new latereasoning::LateReasoningCompiler(dynamic_cast (scopeIntrpr->function), ctx); + //Fill up a dict of all possible specializations + const std::list& specs = ast->getFnSpecializations(calleeName); + std::map dictSpecializations; + for(ManagedFnPtr specFn : specs) { + const TypeAnnotation& specT = specFn->guard.type; + assert(specFn->guard.isValid()); + assert(specT.__operator == TypeOperator::VARIANT); + + unsigned int variantId = specFn->guard.getValueDouble(); + std::string specStr = specT.fields.at(variantId); + dictSpecializations.emplace(specStr, specFn); + } + + auto supply = query->getFnSupply(ASTSite{opCall.id}); + Gringo::Symbol supplyRaw = std::get<0>(supply); + ExpandedType supplyT = std::get<1>(supply); + Expression supplyE = analysis::representTransExpression(supplyRaw, supplyT, transcend); + + const std::string& specStr = supplyT->fields.at(supplyE.getValueDouble()); + ManagedFnPtr specFn = dictSpecializations.at(specStr); + const ExpandedType supplyDataT = ast->expandType(supplyT->__operands.at(supplyE.getValueDouble())); - return new PolymorphFnInvocation(selector, specializations, Parent::pass, - query, Parent::pass->man->llvm, compiler); + compilation::BruteFnInvocation* fnInvocDefault = new compilation::BruteFnInvocation( + Parent::pass->getFunctionUnit(specFn)->compile(), llvm + ); + if (!supplyDataT->isValid()){ + return fnInvocDefault; + } + + const Expression& supplyDataE = getVariantData(supplyE, supplyT); + llvm::Value* supplyDataRaw = Parent::process(supplyDataE); + return new compilation::HiddenArgsFnInvocation({supplyDataRaw}, fnInvocDefault); } } ; +template +class PolymorphBruteFnDecorator: public Parent{ +public: + PolymorphBruteFnDecorator(ManagedFnPtr f, CompilePass *p): Parent(f, p){} + +protected: + std::vector + prepareSignature() override { + std::vector &&signature = Parent::prepareSignature(); + if (!Parent::function->guard.isValid()) return signature; + + AST* ast = Parent::pass->man->root; + unsigned int guardDataId = signature.size() + 1; + const Expression& guardE = Parent::function->guard; + assert(guardE.op == Operator::VARIANT); + + const ExpandedType guardT = ast->expandType(guardE.type.__operands.at(guardE.getValueDouble())); + if (!guardT->isValid()) return signature; + if (!guardE.bindings.size()) return signature; + assert(guardE.bindings.size() == 1); + + std::string guardArgName = guardE.bindings.at(0); + llvm::Type *guardTRaw = Parent::pass->man->llvm->toLLVMType(guardT); + + Expression argBindE; + argBindE.type = guardT; + + Parent::function->addBinding( + Atom(std::move(guardArgName)), + std::move(argBindE), + guardDataId + ); + signature.push_back(guardTRaw); + return signature; + } +}; + } } //end of xreate::polymorph #endif /* POLYMORPHCOMPILER_H */ diff --git a/cpp/src/compilation/targetinterpretation.cpp b/cpp/src/compilation/targetinterpretation.cpp index eef0aae..a41bac5 100644 --- a/cpp/src/compilation/targetinterpretation.cpp +++ b/cpp/src/compilation/targetinterpretation.cpp @@ -1,626 +1,648 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: targetinterpretation.cpp * Author: pgess * * Created on June 29, 2016, 6:45 PM */ /** * \file targetinterpretation.h * \brief Interpretation support. See more details on [Interpretation](/d/concepts/interpretation/) */ #include "compilation/targetinterpretation.h" #include "pass/interpretationpass.h" #include "analysis/typeinference.h" #include "llvmlayer.h" -#include "compilation/scopedecorators.h" -#include "compilation/interpretation-instructions.h" +#include "compilation/decorators.h" +#include "compilation/i12ninst.h" +#include "compilation/intrinsics.h" #include #include #include using namespace std; using namespace xreate::compilation; namespace xreate{ namespace interpretation{ const Expression EXPRESSION_FALSE = Expression(Atom(0)); const Expression EXPRESSION_TRUE = Expression(Atom(1)); CodeScope* InterpretationScope::processOperatorIf(const Expression& expression) { const Expression& exprCondition = process(expression.getOperands()[0]); if (exprCondition == EXPRESSION_TRUE) { return expression.blocks.front(); } return expression.blocks.back(); } CodeScope* InterpretationScope::processOperatorSwitch(const Expression& expression) { const Expression& exprCondition = process(expression.operands[0]); bool flagHasDefault = expression.operands[1].op == Operator::CASE_DEFAULT; //TODO check that one and only one case variant is appropriate for (size_t size = expression.operands.size(), i = flagHasDefault ? 2 : 1; i < size; ++i) { const Expression& exprCase = process(expression.operands[i]); if (function->getScope((const CodeScope*) exprCase.blocks.front())->processScope() == exprCondition) { return exprCase.blocks.back(); } } if (flagHasDefault) { const Expression& exprCaseDefault = expression.operands[1]; return exprCaseDefault.blocks.front(); } assert(false && "Switch has no appropriate variant"); return nullptr; } CodeScope* InterpretationScope::processOperatorSwitchVariant(const Expression& expression) { - const Expression& condition = process(expression.operands.at(0)); - assert(condition.op == Operator::VARIANT); - const string& identCondition = expression.bindings.front(); - - Expression opExpected(Atom(condition.getValueDouble())); - auto itFoundValue = std::find(++expression.operands.begin(), expression.operands.end(), opExpected); - assert(itFoundValue != expression.operands.end()); - - int indexBlock = itFoundValue - expression.operands.begin() - 1; - auto blockFound = expression.blocks.begin(); - std::advance(blockFound, indexBlock); - - InterpretationScope* scopeI12n = function->getScope(*blockFound); - - if(condition.operands.size()) { - const Expression& value = condition.operands.at(0); - scopeI12n->overrideBindings({ - {value, identCondition} - }); - } - - return *blockFound; + const ExpandedType& conditionT = function->__pass->man->root->getType(expression.operands.at(0)); + const Expression& conditionE = process(expression.operands.at(0)); + assert(conditionE.op == Operator::VARIANT); + const string& aliasS = expression.bindings.front(); + + unsigned caseExpectedId = (int) conditionE.getValueDouble(); + auto itFoundValue = std::find_if(++expression.operands.begin(), expression.operands.end(), [caseExpectedId](const auto& caseActualE){ + return (unsigned) caseActualE.getValueDouble() == caseExpectedId; + }); + assert(itFoundValue != expression.operands.end()); + + int caseScopeId = itFoundValue - expression.operands.begin() - 1; + auto caseScopeRef = expression.blocks.begin(); + std::advance(caseScopeRef, caseScopeId); + InterpretationScope* scopeI12n = function->getScope(*caseScopeRef); + + if(conditionE.operands.size()) { + Expression valueE(Operator::LIST, {}); + valueE.operands = conditionE.operands; + valueE.bindings = conditionT->__operands.at(caseExpectedId).fields; + + scopeI12n->overrideBindings({ + {valueE, aliasS} + }); + }; + + return *caseScopeRef; } llvm::Value* -InterpretationScope::processLate(const InterpretationOperator& op, const Expression& expression, const Context& context) { +InterpretationScope::processLate(const InterpretationOperator& op, const Expression& expression, const Context& context, const std::string& hintAlias) { switch(op) { case IF_INTERPRET_CONDITION: { CodeScope* scopeResult = processOperatorIf(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_INTERPRET_CONDITION: { CodeScope* scopeResult = processOperatorSwitch(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); - const Expression& condition = expression.operands.at(0); - const Expression& valueCondition = process(condition); + const Expression& condCrudeE = expression.operands.at(0); + const Expression& condE = process(condCrudeE); const string identCondition = expression.bindings.front(); auto scopeCompilation = Decorators::getInterface(context.function->getScopeUnit(scopeResult)); - if(valueCondition.operands.size()) { + if(condE.operands.size()) { //override value Symbol symbCondition{ScopedSymbol{scopeResult->__identifiers.at(identCondition), versions::VERSION_NONE}, scopeResult}; scopeCompilation->overrideDeclarations({ - {symbCondition, Expression(valueCondition.operands.at(0))}} + {symbCondition, Expression(condE.operands.at(0))}} ); //set correct type for binding: - TypeAnnotation typeVariant = typeinference::getType(condition, *function->man->ast); - int conditionIndex = valueCondition.getValueDouble(); + TypeAnnotation typeVariant = function->__pass->man->root->getType(condCrudeE); + int conditionIndex = condE.getValueDouble(); ScopedSymbol symbolInternal = scopeResult->getSymbol(identCondition); scopeResult->__declarations[symbolInternal].bindType(typeVariant.__operands.at(conditionIndex)); } llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_LATE: { - latereasoning::LateReasoningCompiler compiler(dynamic_cast(this->function), context); - return compiler.processSwitchLateStatement(expression, ""); + return nullptr; +// latereasoning::LateReasoningCompiler compiler(dynamic_cast(this->function), context); +// return compiler.processSwitchLateStatement(expression, ""); } case FOLD_INTERPRET_INPUT: { - //initialization - const Expression& exprInput = process(expression.getOperands()[0]); - assert(exprInput.op == Operator::LIST); - - CodeScope* scopeBody = expression.blocks.front(); - - const string& nameEl = expression.bindings[0]; - Symbol symbEl{ScopedSymbol{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody}; - const std::string& idAccum = expression.bindings[1]; - llvm::Value* rawAccum = context.scope->process(expression.getOperands()[1]); - - InterpretationScope* intrBody = function->getScope(scopeBody); - auto unitBody = Decorators::getInterface(context.function->getScopeUnit(scopeBody)); - - const std::vector elementsInput = exprInput.getOperands(); - - for(size_t i = 0; i < elementsInput.size(); ++i) { - const Expression& exprElement = elementsInput[i]; - - intrBody->overrideBindings({ - {exprElement, nameEl} - }); - unitBody->overrideDeclarations({ - {symbEl, exprElement} - }); //resets unitBody - unitBody->bindArg(rawAccum, string(idAccum)); + //initialization + const Expression& containerE = process(expression.getOperands().at(0)); + const TypeAnnotation& accumT = expression.type; + assert(containerE.op == Operator::LIST); + CodeScope* bodyScope = expression.blocks.front(); + const string& elAlias = expression.bindings[0]; + Symbol elS{ScopedSymbol{bodyScope->__identifiers.at(elAlias), versions::VERSION_NONE}, bodyScope}; + const std::string& accumAlias = expression.bindings[1]; + llvm::Value* accumRaw = context.scope->process(expression.getOperands().at(1), accumAlias, accumT); + + InterpretationScope* bodyI12n = function->getScope(bodyScope); + auto bodyBrute = Decorators::getInterface(context.function->getScopeUnit(bodyScope)); + const std::vector& containerVec = containerE.getOperands(); + + for(size_t i = 0; i < containerVec.size(); ++i) { + const Expression& elE = containerVec[i]; + + bodyI12n->overrideBindings({ + {elE, elAlias} + }); + bodyBrute->overrideDeclarations({ + {elS, elE} + }); //resets bodyBrute + bodyBrute->bindArg(accumRaw, string(accumAlias)); - rawAccum = unitBody->compile(); - } + accumRaw = bodyBrute->compile(); + accumRaw->getType()->print(llvm::outs()); + llvm::outs() << "\n\n"; + } - return rawAccum; + return accumRaw; } // case FOLD_INF_INTERPRET_INOUT: // { // } //TODO refactor as InterpretationCallStatement class case CALL_INTERPRET_PARTIAL: { const std::string &calleeName = expression.getValueString(); - ICodeScopeUnit* scopeUnitSelf = context.scope; - ManagedFnPtr callee = this->function->man->ast->findFunction(calleeName); - const FunctionInterpretationData& calleeData = FunctionInterpretationHelper::getSignature(callee); + IBruteScope* scopeUnitSelf = context.scope; + ManagedFnPtr callee = this->function->__pass->man->root->findFunction(calleeName); + const I12nFunctionSpec& calleeData = FunctionInterpretationHelper::getSignature(callee); std::vector argsActual; - PIFSignature sig; + PIFnSignature sig; sig.declaration = callee; for(size_t no = 0, size = expression.operands.size(); no < size; ++no) { const Expression& op = expression.operands[no]; if (calleeData.signature.at(no) == INTR_ONLY) { sig.bindings.push_back(process(op)); continue; } argsActual.push_back(scopeUnitSelf->process(op)); } - TargetInterpretation* man = dynamic_cast (this->function->man); + TargetInterpretation* man = dynamic_cast (this->function->__pass); PIFunction* pifunction = man->getFunction(move(sig)); llvm::Function* raw = pifunction->compile(); boost::scoped_ptr statement(new BruteFnInvocation(raw, man->pass->man->llvm)); return (*statement)(move(argsActual)); } case QUERY_LATE: { - return IntrinsicQueryInstruction( - dynamic_cast(this->function)) - .processLate(expression, context); + return nullptr; +// return IntrinsicQueryInstruction( +// dynamic_cast(this->function)) +// .processLate(expression, context); } default: break; } assert(false && "Unknown late interpretation operator"); return nullptr; } llvm::Value* -InterpretationScope::compile(const Expression& expression, const Context& context) { +InterpretationScope::compile(const Expression& expression, const Context& context, const std::string& hintAlias) { const InterpretationData& data = Attachments::get(expression); if (data.op != InterpretationOperator::NONE) { - return processLate(data.op, expression, context); + return processLate(data.op, expression, context, hintAlias); } Expression result = process(expression); - return context.scope->process(result); + return context.scope->process(result, hintAlias); } Expression InterpretationScope::process(const Expression& expression) { #ifndef NDEBUG if (expression.tags.count("bpoint")) { std::raise(SIGINT); } #endif - PassManager* man = (static_cast (function->man))->pass->man; + PassManager* man = function->__pass->man; switch (expression.__state) { case Expression::INVALID: assert(false); case Expression::NUMBER: case Expression::STRING: return expression; case Expression::IDENT: { Symbol s = Attachments::get(expression); return Parent::processSymbol(s); } case Expression::COMPOUND: break; default: assert(false); } switch (expression.op) { case Operator::EQU: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_TRUE; return EXPRESSION_FALSE; } case Operator::NE: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_FALSE; return EXPRESSION_TRUE; } case Operator::LOGIC_AND: { assert(expression.operands.size() == 1); return process (expression.operands[0]); } // case Operator::LOGIC_OR: case Operator::CALL: { const std::string &fnName = expression.getValueString(); - ManagedFnPtr fnAst = this->function->man->ast->findFunction(fnName); - InterpretationFunction* fnUnit = this->function->man->getFunction(fnAst); + ManagedFnPtr fnAst = man->root->findFunction(fnName); + InterpretationFunction* fnUnit = this->function->__pass->getFunction(fnAst); vector args; args.reserve(expression.getOperands().size()); for(size_t i = 0, size = expression.getOperands().size(); i < size; ++i) { args.push_back(process(expression.getOperands()[i])); } return fnUnit->process(args); } case Operator::CALL_INTRINSIC: { - assert(false && "Unknown intrinsic"); + const Expression& opCallIntrCrude = expression; + vector argsActual; + argsActual.reserve(opCallIntrCrude.getOperands().size()); + for(const auto& op: opCallIntrCrude.getOperands()) { + argsActual.push_back(process(op)); + } + + Expression opCallIntr(Operator::CALL_INTRINSIC, {}); + opCallIntr.setValueDouble(opCallIntrCrude.getValueDouble()); + opCallIntr.operands = argsActual; + + compilation::IntrinsicCompiler compiler(man); + return compiler.interpret(opCallIntr); } case Operator::QUERY: { - return IntrinsicQueryInstruction(dynamic_cast(this->function)) - .process(expression); + return Expression(); +// return IntrinsicQueryInstruction(dynamic_cast(this->function)) +// .process(expression); } case Operator::QUERY_LATE: { assert(false && "Can't be interpretated"); return Expression(); } case Operator::IF: { CodeScope* scopeResult = processOperatorIf(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH: { CodeScope* scopeResult = processOperatorSwitch(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); return function->getScope(scopeResult)->processScope(); } case Operator::VARIANT: { - if(!expression.operands.size()) return expression; + Expression result{Operator::VARIANT, {}}; + result.setValueDouble(expression.getValueDouble()); - Expression variantData = process(expression.operands[0]); - Expression result{Operator::VARIANT, {variantData}}; - result.setValueDouble(expression.getValueDouble()); - return result; + for(const Expression& op: expression.operands){ + result.operands.push_back(process(op)); + } + + return result; } case Operator::INDEX: { - Expression exprData = process(expression.operands[0]); + Expression aggrE = process(expression.operands[0]); - for (size_t keyId = 1; keyId < expression.operands.size(); ++keyId) { - const Expression& exprKey = process(expression.operands[keyId]); + for (size_t keyId = 1; keyId < expression.operands.size(); ++keyId) { + const Expression& keyE = process(expression.operands[keyId]); - if (exprKey.__state == Expression::STRING) { - const string& key = exprKey.getValueString(); - assert(exprData.__indexBindings.count(key)); - size_t idxKey = exprData.__indexBindings.at(key); - - exprData = Expression(exprData.operands.at(idxKey)); - continue; - } - - if (exprKey.__state == Expression::NUMBER) { - int key = exprKey.getValueDouble(); - exprData = Expression(exprData.operands[key]); - continue; + if (keyE.__state == Expression::STRING) { + const string& fieldExpectedS = keyE.getValueString(); + unsigned fieldId; + for(fieldId = 0; fieldId < aggrE.bindings.size(); ++fieldId){ + if (aggrE.bindings.at(fieldId) == fieldExpectedS){break;} } + assert(fieldId < aggrE.bindings.size()); + aggrE = Expression(aggrE.operands.at(fieldId)); + continue; + } - assert(false && "Inappropriate key"); + if (keyE.__state == Expression::NUMBER) { + int opId = keyE.getValueDouble(); + aggrE = Expression(aggrE.operands.at(opId)); + continue; } - return exprData; + assert(false && "Inappropriate key"); + } + + return aggrE; } case Operator::FOLD: { const Expression& exprInput = process(expression.getOperands()[0]); const Expression& exprInit = process(expression.getOperands()[1]); const std::string& argEl = expression.bindings[0]; const std::string& argAccum = expression.bindings[1]; InterpretationScope* body = function->getScope(expression.blocks.front()); Expression accum = exprInit; for(size_t size = exprInput.getOperands().size(), i = 0; i < size; ++i) { body->overrideBindings({ {exprInput.getOperands()[i], argEl}, {accum, argAccum} }); accum = body->processScope(); } return accum; } case Operator::LIST: case Operator::LIST_RANGE: { Expression result(expression.op,{}); result.operands.resize(expression.operands.size()); result.bindings = expression.bindings; - result.__indexBindings = expression.__indexBindings; int keyId = 0; for(const Expression& opCurrent : expression.operands) { result.operands[keyId++] = process(opCurrent); } return result; } // case Operator::MAP: { // break; // } default: break; } return expression; } InterpretationFunction* -TargetInterpretation::getFunction(IFunctionUnit* unit) { +TargetInterpretation::getFunction(IBruteFunction* unit) { if (__dictFunctionsByUnit.count(unit)) { return __dictFunctionsByUnit.at(unit); } InterpretationFunction* f = new InterpretationFunction(unit->function, this); __dictFunctionsByUnit.emplace(unit, f); assert(__functions.emplace(unit->function.id(), f).second); return f; } PIFunction* -TargetInterpretation::getFunction(PIFSignature&& sig) { +TargetInterpretation::getFunction(PIFnSignature&& sig) { auto f = __pifunctions.find(sig); if (f != __pifunctions.end()) { return f->second; } - PIFunction* result = new PIFunction(PIFSignature(sig), __pifunctions.size(), this); + PIFunction* result = new PIFunction(PIFnSignature(sig), __pifunctions.size(), this); __pifunctions.emplace(move(sig), result); - assert(__dictFunctionsByUnit.emplace(result->functionUnit, result).second); + assert(__dictFunctionsByUnit.emplace(result->fnRaw, result).second); return result; } InterpretationScope* TargetInterpretation::transformContext(const Context& c) { return this->getFunction(c.function)->getScope(c.scope->scope); } llvm::Value* -TargetInterpretation::compile(const Expression& expression, const Context& ctx) { - return transformContext(ctx)->compile(expression, ctx); +TargetInterpretation::compile(const Expression& expression, const Context& ctx, const std::string& hintAlias) { + return transformContext(ctx)->compile(expression, ctx, hintAlias); } InterpretationFunction::InterpretationFunction(const ManagedFnPtr& function, Target* target) : Function(function, target) { } Expression InterpretationFunction::process(const std::vector& args) { InterpretationScope* body = getScope(__function->__entry); list> bindings; for(size_t i = 0, size = args.size(); i < size; ++i) { bindings.push_back(make_pair(args.at(i), body->scope->__bindings.at(i))); } body->overrideBindings(bindings); return body->processScope(); } // Partial function interpretation -typedef BasicFunctionUnit PIFunctionUnitParent; +typedef BasicBruteFunction BruteFunction; -class PIFunctionUnit : public PIFunctionUnitParent{ +class PIBruteFunction : public BruteFunction{ public: - - PIFunctionUnit(ManagedFnPtr f, std::set&& arguments, size_t id, CompilePass* p) - : PIFunctionUnitParent(f, p), argumentsActual(move(arguments)), __id(id) { } + PIBruteFunction(ManagedFnPtr f, std::set&& arguments, size_t id, CompilePass* p) + : BruteFunction(f, p), argumentsActual(move(arguments)), __id(id) { } protected: - std::vector prepareSignature() override { - LLVMLayer* llvm = PIFunctionUnitParent::pass->man->llvm; - AST* ast = PIFunctionUnitParent::pass->man->root; - CodeScope* entry = PIFunctionUnitParent::function->__entry; + LLVMLayer* llvm = BruteFunction::pass->man->llvm; + AST* ast = BruteFunction::pass->man->root; + CodeScope* entry = BruteFunction::function->__entry; std::vector signature; for(size_t no : argumentsActual) { VNameId argId = entry->__identifiers.at(entry->__bindings.at(no)); ScopedSymbol arg{argId, versions::VERSION_NONE}; signature.push_back(llvm->toLLVMType(ast->expandType(entry->__declarations.at(arg).type))); } return signature; } llvm::Function::arg_iterator prepareBindings() override{ - CodeScope* entry = PIFunctionUnitParent::function->__entry; - ICodeScopeUnit* entryCompilation = PIFunctionUnitParent::getScopeUnit(entry); - llvm::Function::arg_iterator fargsI = PIFunctionUnitParent::raw->arg_begin(); + CodeScope* entry = BruteFunction::function->__entry; + IBruteScope* entryCompilation = BruteFunction::getScopeUnit(entry); + llvm::Function::arg_iterator fargsI = BruteFunction::raw->arg_begin(); for(size_t no : argumentsActual) { ScopedSymbol arg{entry->__identifiers.at(entry->__bindings.at(no)), versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, arg); fargsI->setName(entry->__bindings.at(no)); ++fargsI; } return fargsI; } virtual std::string prepareName() override { - return PIFunctionUnitParent::prepareName() + "_" + std::to_string(__id); + return BruteFunction::prepareName() + "_" + std::to_string(__id); } private: std::set argumentsActual; size_t __id; } ; -PIFunction::PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target) -: InterpretationFunction(sig.declaration, target), signatureInstance(move(sig)) { - const FunctionInterpretationData& functionData = FunctionInterpretationHelper::getSignature(signatureInstance.declaration); +PIFunction::PIFunction(PIFnSignature&& sig, size_t id, TargetInterpretation* target) +: InterpretationFunction(sig.declaration, target), instance(move(sig)) { + const I12nFunctionSpec& functionData = FunctionInterpretationHelper::getSignature(instance.declaration); std::set argumentsActual; for (size_t no = 0, size = functionData.signature.size(); no < size; ++no) { if (functionData.signature.at(no) != INTR_ONLY) { argumentsActual.insert(no); } } - functionUnit = new PIFunctionUnit(signatureInstance.declaration, move(argumentsActual), id, target->pass); - CodeScope* entry = signatureInstance.declaration->__entry; - auto entryUnit = Decorators::getInterface<>(functionUnit->getEntry()); + fnRaw = new PIBruteFunction(instance.declaration, move(argumentsActual), id, target->pass); + CodeScope* entry = instance.declaration->__entry; + auto entryUnit = Decorators::getInterface<>(fnRaw->getEntry()); InterpretationScope* entryIntrp = InterpretationFunction::getScope(entry); list> bindingsPartial; list> declsPartial; for(size_t no = 0, sigNo = 0, size = entry->__bindings.size(); no < size; ++no) { if(functionData.signature.at(no) == INTR_ONLY) { - bindingsPartial.push_back({signatureInstance.bindings[sigNo], entry->__bindings[no]}); + bindingsPartial.push_back({instance.bindings[sigNo], entry->__bindings[no]}); VNameId argId = entry->__identifiers.at(entry->__bindings[no]); Symbol argSymbol{ScopedSymbol {argId, versions::VERSION_NONE}, entry}; - declsPartial.push_back({argSymbol, signatureInstance.bindings[sigNo]}); + declsPartial.push_back({argSymbol, instance.bindings[sigNo]}); ++sigNo; } } entryIntrp->overrideBindings(bindingsPartial); entryUnit->overrideDeclarations(declsPartial); } llvm::Function* PIFunction::compile() { - llvm::Function* raw = functionUnit->compile(); + llvm::Function* raw = fnRaw->compile(); return raw; } -bool operator<(const PIFSignature& lhs, const PIFSignature& rhs) { +bool operator<(const PIFnSignature& lhs, const PIFnSignature& rhs) { if (lhs.declaration.id() != rhs.declaration.id()) { return lhs.declaration.id() < rhs.declaration.id(); } return lhs.bindings < rhs.bindings; } -bool operator<(const PIFSignature& lhs, PIFunction * const rhs) { - return lhs < rhs->signatureInstance; +bool operator<(const PIFnSignature& lhs, PIFunction * const rhs) { + return lhs < rhs->instance; } -bool operator<(PIFunction * const lhs, const PIFSignature& rhs) { - return lhs->signatureInstance < rhs; +bool operator<(PIFunction * const lhs, const PIFnSignature& rhs) { + return lhs->instance < rhs; } } } /** \class xreate::interpretation::InterpretationFunction * * Holds list of xreate::interpretation::InterpretationScope 's focused on interpretation of individual code scopes * * There is particulat subclass PIFunction intended to represent partially interpreted functions. *\sa TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/) */ /** \class xreate::interpretation::TargetInterpretation * * TargetInterpretation is executed during compilation and is intended to preprocess eligible for interpretation parts of a source code. * * Keeps a list of InterpretationFunction / PIFunction that represent interpretation for an individual functions. * * There is \ref InterpretationScopeDecorator that embeds interpretation to an overall compilation process. * \sa InterpretationPass, compilation::Target, [Interpretation Concept](/d/concepts/interpretation/) * */ diff --git a/cpp/src/compilation/targetinterpretation.h b/cpp/src/compilation/targetinterpretation.h index 8ff000b..99c4089 100644 --- a/cpp/src/compilation/targetinterpretation.h +++ b/cpp/src/compilation/targetinterpretation.h @@ -1,133 +1,132 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: targetstatic.h * Author: pgess * * Created on July 2, 2016, 1:25 PM */ #ifndef TARGETSTATIC_H #define TARGETSTATIC_H #include "ast.h" #include "pass/compilepass.h" #include "compilation/targets.h" #include "pass/interpretationpass.h" #include "transcendlayer.h" namespace xreate{ namespace interpretation{ class TargetInterpretation; class InterpretationScope; class InterpretationFunction; }} namespace xreate{ namespace compilation{ template <> struct TargetInfo { typedef Expression Result; typedef interpretation::InterpretationScope Scope; typedef interpretation::InterpretationFunction Function; }; }} namespace xreate{ namespace interpretation{ /** \brief Encapsulates interpretation of a single code scope */ class InterpretationScope: public compilation::Scope{ typedef Scope Parent; public: InterpretationScope(const CodeScope* scope, compilation::Function* f): Parent(scope, f) {} Expression process(const Expression& expression) override; - llvm::Value* compile(const Expression& expression, const compilation::Context& context); + llvm::Value* compile(const Expression& expression, const compilation::Context& context, const std::string& hintAlias); private: - llvm::Value* processLate(const InterpretationOperator& op, const Expression& expression, const compilation::Context& context); + llvm::Value* processLate(const InterpretationOperator& op, const Expression& expression, const compilation::Context& context, const std::string& hintAlias); //llvm::Value* compilePartialFnCall(const Expression& expression, const Context& context); CodeScope* processOperatorIf(const Expression& expression); CodeScope* processOperatorSwitch(const Expression& expression); CodeScope* processOperatorSwitchVariant(const Expression& expression); }; /** \brief Encapsulates interpretation of a single function */ class InterpretationFunction: public compilation::Function{ public: InterpretationFunction(const ManagedFnPtr& function, compilation::Target* target); Expression process(const std::vector& args); }; /** \brief Signature of a partially interpreted function */ -struct PIFSignature{ +struct PIFnSignature{ ManagedFnPtr declaration; std::vector bindings; }; -class PIFunctionUnit; +class PIBruteFunction; /** \brief Encapsulates partially interpreted function */ class PIFunction: public InterpretationFunction{ public: - PIFunctionUnit* functionUnit; - PIFSignature signatureInstance; + PIBruteFunction* fnRaw; + PIFnSignature instance; - PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target); + PIFunction(PIFnSignature&& sig, size_t id, TargetInterpretation* target); llvm::Function* compile(); }; -bool operator<(const PIFSignature& lhs, PIFunction* const rhs); -bool operator<(PIFunction* const lhs, const PIFSignature& rhs); +bool operator<(const PIFnSignature& lhs, PIFunction* const rhs); +bool operator<(PIFunction* const lhs, const PIFnSignature& rhs); /** \brief Encapsulates interpretation process based on analysis results from \ref InterpretationPass * \extends compilation::Target * \sa InterpretationPass */ class TargetInterpretation: public compilation::Target{ -public: - TargetInterpretation(AST* root, CompilePass* passCompilation): Target(root), pass(passCompilation){} + typedef compilation::Target Parent; - //target: -public: - InterpretationFunction* getFunction(compilation::IFunctionUnit* unit); - PIFunction* getFunction(PIFSignature&& sig); +public: + TargetInterpretation(PassManager *man, CompilePass* passCompilation): Parent(man), pass(passCompilation){} + InterpretationFunction* getFunction(compilation::IBruteFunction* unit); + PIFunction* getFunction(PIFnSignature&& sig); private: - std::map __pifunctions; - std::map __dictFunctionsByUnit; + std::map __pifunctions; + std::map __dictFunctionsByUnit; - //self: + //self: public: - CompilePass* pass; - llvm::Value* compile(const Expression& expression, const compilation::Context& ctx); - InterpretationScope* transformContext(const compilation::Context& c); + CompilePass* pass; + llvm::Value* compile(const Expression& expression, const compilation::Context& ctx, const std::string& hintAlias); + InterpretationScope* transformContext(const compilation::Context& c); private: }; /**\brief Interpretation-aware Code Scope decorator - * \extends xreate::compilation::ICodeScopeUnit + * \extends xreate::compilation::IBruteScope */ template class InterpretationScopeDecorator: public Parent{ public: - InterpretationScopeDecorator(const CodeScope* const codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} + InterpretationScopeDecorator(const CodeScope* const codeScope, compilation::IBruteFunction* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} - virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl){ + virtual llvm::Value* process(const Expression& expr, const std::string& hintAlias, const TypeAnnotation& expectedT = TypeAnnotation()){ const InterpretationData& data = Attachments::get(expr, {ANY, NONE}); bool flagInterpretationEligible = (data.resolution == INTR_ONLY || data.op != InterpretationOperator::NONE); if (flagInterpretationEligible){ compilation::Context ctx{this, this->function, this->pass}; - return Parent::pass->targetInterpretation->compile(expr, ctx); + return Parent::pass->targetInterpretation->compile(expr, ctx, hintAlias); } - return Parent::process(expr, hintVarDecl); + return Parent::process(expr, hintAlias, expectedT); } }; }} //end of xreate:: interpretation #endif /* TARGETSTATIC_H */ \ No newline at end of file diff --git a/cpp/src/compilation/targets.h b/cpp/src/compilation/targets.h index 9a0b8cd..ebf5bd1 100644 --- a/cpp/src/compilation/targets.h +++ b/cpp/src/compilation/targets.h @@ -1,223 +1,208 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: targetabstract.h * Author: pgess * * Created on July 2, 2016, 1:25 PM */ /** * \file * \brief Compilation targets infrastructure */ #ifndef TARGETABSTRACT_H #define TARGETABSTRACT_H +class Function; + #include "ast.h" +#include "pass/ipass.h" +//#include "targetinterpretation.h" #include #include #include namespace xreate{ namespace compilation{ -template +template struct TargetInfo{ - //typedef Result - //typedef Function - //typedef Scope +//public: +// typedef Result +// typedef Function +// typedef Scope }; template class Function; template class Target; -template +template class Scope{ - typedef typename TargetInfo::Scope Self; + typedef typename TargetInfo::Scope Self; public: const CodeScope* scope; - Function* function = 0; + Function* function = 0; - typename TargetInfo::Result + typename TargetInfo::Result processSymbol(const Symbol& s){ const CodeScope* scope = s.scope; Self* self = function->getScope(scope); if(self->__bindings.count(s.identifier)){ return self->__bindings[s.identifier]; } const Expression& declaration = CodeScope::getDefinition(s, true); if(!declaration.isDefined()){ assert(false); //for bindings there should be result already } return self->__bindings[s.identifier] = self->process(declaration); } - typename TargetInfo::Result + typename TargetInfo::Result processScope(){ return process(scope->getBody()); } - // typename TargetInfo::Result - // processFunction(typename TargetInfo::Function* fnRemote, const std::vector::Result>& args){ - // Scope scopeRemote = fnRemote->getScope(fnRemote->__function->__entry); - // - // if (scopeRemote->raw){ - // return scopeRemote->raw; - // } - // - // return fnRemote->process(args); - // } - - virtual typename TargetInfo::Result + virtual typename TargetInfo::Result process(const Expression& expression) = 0; - Scope(const CodeScope* codeScope, Function* f) + Scope(const CodeScope* codeScope, Function* f) : scope(codeScope), function(f){ } virtual ~Scope(){ } void - overrideBindings(std::list::Result, std::string>> bindings){ - std::list < std::pair::Result, ScopedSymbol>> bindingsSymbols; + overrideBindings(std::list::Result, std::string>> bindings){ + std::list < std::pair::Result, ScopedSymbol>> bindingsSymbols; for(auto entry: bindings){ assert(scope->__identifiers.count(entry.second)); ScopedSymbol id{scope->__identifiers.at(entry.second), versions::VERSION_NONE}; bindingsSymbols.push_back(std::make_pair(entry.first, id)); } overrideBindings(bindingsSymbols); } void - overrideBindings(std::list::Result, ScopedSymbol>> bindings){ + overrideBindings(std::list::Result, ScopedSymbol>> bindings){ reset(); for(auto entry: bindings){ __bindings[entry.second] = entry.first; } } bool isBindingDefined(const ScopedSymbol& id){ return __bindings.count(id); } void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __bindings.clear(); __childScopes.clear(); } protected: - std::map::Result> __bindings; + std::map::Result> __bindings; std::list> __childScopes; }; -template +template class Function{ - typedef typename TargetInfo::Result Result; - typedef typename TargetInfo::Scope ConcreteScope; + typedef typename TargetInfo::Result Result; + typedef typename TargetInfo::Scope ConcreteScope; public: - - Function(const ManagedFnPtr& function, Target* target) - : man(target), __function(function){ } - - virtual - ~Function(){ }; + Function(const ManagedFnPtr& function, Target* target) + : __pass(target), __function(function){ } + virtual ~Function(){ }; + virtual Result process(const std::vector& args) = 0; ConcreteScope* getScope(const CodeScope * const scope){ if(__scopes.count(scope)){ auto result = __scopes.at(scope).lock(); if(result){ return result.get(); } } std::shared_ptr unit(new ConcreteScope(scope, this)); if(scope->__parent != nullptr){ getScope(scope->__parent)->registerChildScope(unit); } else{ assert(!__entryScope); __entryScope = unit; } if(!__scopes.emplace(scope, unit).second){ __scopes[scope] = unit; } return unit.get(); } - virtual Result - process(const std::vector& args) = 0; - - Target* man = 0; - ManagedFnPtr __function; + Target* __pass = 0; protected: + + ManagedFnPtr __function; std::map> __scopes; std::shared_ptr __entryScope; }; /** \brief Similar to xreate::IPass */ -template -class Target{ - typedef typename TargetInfo::Function ConcreteFunction; +template +class Target: public IPass{ + typedef typename TargetInfo::Function ConcreteFunction; public: + Target(PassManager *manager): IPass(manager){ } + virtual ~Target(){ + for(const auto& entry: __functions){ + delete entry.second; + } + } + virtual void run() override {} - Target(AST* root): ast(root){ } - - ConcreteFunction* - getFunction(const ManagedFnPtr& function){ + ConcreteFunction* getFunction(const ManagedFnPtr& function){ unsigned int id = function.id(); if(!__functions.count(id)){ ConcreteFunction* unit = new ConcreteFunction(function, this); __functions.emplace(id, unit); return unit; } return __functions.at(id); } - AST* ast; - - virtual - ~Target(){ - for(const auto& entry: __functions){ - delete entry.second; - } - } - protected: std::map __functions; }; } } #endif /* TARGETABSTRACT_H */ diff --git a/cpp/src/compilation/transformations.h b/cpp/src/compilation/transformations.h index 2dcf8bc..fb33230 100644 --- a/cpp/src/compilation/transformations.h +++ b/cpp/src/compilation/transformations.h @@ -1,116 +1,116 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: transformations.h * Author: pgess * * Created on March 25, 2017, 9:04 PM */ #ifndef TRANSFORMATIONS_H #define TRANSFORMATIONS_H #include "pass/compilepass.h" namespace xreate { namespace compilation { template struct TransformerInfo { //static const unsigned int id = 1; (current vacant id) }; class Transformer{ public: virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx)=0; virtual ~Transformer(){}; }; class TransformationsManager { public: std::list getRelevantTransformers(const Expression& expression); template void registerTransformer(const std::string& annotation, TransformerType* t){ const int id = TransformerInfo::id; assert(!__transformers.count(id)); __transformers[id] = t; __subscriptions.emplace(annotation, id); } template void unregisterTransformer(const std::string& annotation, TransformerType* t){ const unsigned int id = TransformerInfo::id; auto range = __subscriptions.equal_range(annotation); const auto entry = make_pair(annotation, id); __subscriptions.erase(std::find_if(range.first, range.second, [id](const auto& el){return el.second == id;})); __transformers.erase(id); } template TransformerType* update(TransformerType* newInstance){ const int id = TransformerInfo::id; Transformer* oldInstance = __transformers[id]; __transformers[id] = newInstance; return static_cast(oldInstance); } template bool exists(){ const int id = TransformerInfo::id; return __transformers.count(id); } template TransformerType* get(){ const int id = TransformerInfo::id; return static_cast(__transformers.at(id)); } private: std::map __transformers; std::multimap __subscriptions; }; /**\brief Provides custom transformations during Code Scope compilation - * \extends xreate::compilation::ICodeScopeUnit + * \extends xreate::compilation::IBruteScope */ template class TransformationsScopeDecorator: public Transformer, public Parent { // SCOPE DECORATOR PART public: - TransformationsScopeDecorator(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass) + TransformationsScopeDecorator(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass) : Parent(codeScope, f, compilePass){} virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl=""){ llvm::Value* result = Parent::process(expr, hintVarDecl); return transform(expr, result, Context{this, Parent::function, Parent::pass}); } // TRANSFORMER PART public: virtual llvm::Value* transform(const Expression& expression, llvm::Value* raw, const Context& ctx) { llvm::Value* result = raw; TransformationsManager* man = Parent::pass->managerTransformations; if (expression.tags.size()) for (Transformer* handler: man->getRelevantTransformers(expression)){ result = handler->transform(expression, result, ctx); } return result; } }; } } #endif /* TRANSFORMATIONS_H */ diff --git a/cpp/src/compilation/transformersaturation.cpp b/cpp/src/compilation/transformersaturation.cpp index 4bf599c..7dfc263 100644 --- a/cpp/src/compilation/transformersaturation.cpp +++ b/cpp/src/compilation/transformersaturation.cpp @@ -1,84 +1,84 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * transformersaturation.cpp * * Author: pgess * Created on March 25, 2017, 10:06 PM */ /** * \file transformersaturation.h * \brief Loop saturation support */ #include "transformersaturation.h" #include "llvmlayer.h" using namespace llvm; namespace xreate { namespace compilation { TransformerSaturation::TransformerSaturation(llvm::BasicBlock* allocationBlock, TransformationsManager* manager) : man(manager), blockAllocation(allocationBlock){ if (man->exists()){ oldInstance = man->update(this); } else { man->registerTransformer("final", this); } } TransformerSaturation::~TransformerSaturation(){ if (oldInstance) { man->update(oldInstance); } else { man->unregisterTransformer("final", this); } } llvm::Value* TransformerSaturation::transform(const Expression& expression, llvm::Value* raw, const Context& ctx){ processBreak(ctx); return raw; } void TransformerSaturation::processBreak(const Context& ctx){ allocateFlag(ctx); //show the saturation flag - llvm::IRBuilder<>& builder = ctx.pass->man->llvm->builder; + llvm::IRBuilder<>& builder = ctx.pass->man->llvm->irBuilder; llvm::Type* tyInt1 = llvm::Type::getInt1Ty(ctx.pass->man->llvm->llvmContext); llvm::Constant* constTrue = llvm::ConstantInt::get(tyInt1, 1); builder.CreateStore(constTrue, flagSaturation, true); } void TransformerSaturation::allocateFlag(const Context& ctx){ //allocation of saturation flag IRBuilder<> builder(blockAllocation, blockAllocation->getFirstInsertionPt()); llvm::Type* tyInt1 = llvm::Type::getInt1Ty(ctx.pass->man->llvm->llvmContext); llvm::Constant* constTrue = llvm::ConstantInt::get(tyInt1, 1); flagSaturation = builder.CreateAlloca(tyInt1, constTrue, "flagSaturation"); llvm::Constant* constFalse = llvm::ConstantInt::get(tyInt1, 0); builder.CreateStore(constFalse, flagSaturation, true); } bool TransformerSaturation::insertSaturationChecks(llvm::BasicBlock* blockContinue, llvm::BasicBlock* blockExit, const Context& ctx){ if (!flagSaturation) return false; - llvm::IRBuilder<>& builder = ctx.pass->man->llvm->builder; + llvm::IRBuilder<>& builder = ctx.pass->man->llvm->irBuilder; builder.CreateCondBr(builder.CreateLoad(flagSaturation), blockExit, blockContinue); return true; } } } diff --git a/cpp/src/llvmlayer.cpp b/cpp/src/llvmlayer.cpp index 846d96c..930d6bf 100644 --- a/cpp/src/llvmlayer.cpp +++ b/cpp/src/llvmlayer.cpp @@ -1,242 +1,284 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * llvmlayer.cpp * * Author: pgess */ /** * \file llvmlayer.h * \brief Bytecode generation */ #include "ast.h" #include "llvmlayer.h" +#include "analysis/typehints.h" + +#ifdef XREATE_ENABLE_EXTERN #include "ExternLayer.h" +#endif + #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/MCJIT.h" #include "llvm/Support/TargetSelect.h" + #include #include using namespace llvm; using namespace xreate; +using namespace xreate::typehints; using namespace std; -LLVMLayer::LLVMLayer(AST* root) -: llvmContext(), -builder(llvmContext), -ast(root), -module(new llvm::Module(root->getModuleName(), llvmContext)) { - layerExtern = new ExternLayer(this); - layerExtern->init(root); +LLVMLayer::LLVMLayer(AST *root) +: llvmContext(), + irBuilder(llvmContext), + ast(root), + module(new llvm::Module(root->getModuleName(), llvmContext)){ + llvm::InitializeNativeTarget(); + llvm::InitializeNativeTargetAsmPrinter(); + llvm::EngineBuilder builder; + TargetMachine *target = builder.selectTarget(); + module->setDataLayout(target->createDataLayout()); + +#ifdef XREATE_ENABLE_EXTERN + layerExtern = new ExternLayer(this); + layerExtern->init(root); +#endif } -void* -LLVMLayer::getFunctionPointer(llvm::Function* function) { - uint64_t entryAddr = jit->getFunctionAddress(function->getName().str()); - return (void*) entryAddr; +void * +LLVMLayer::getFunctionPointer(llvm::Function *function){ + uint64_t entryAddr = jit->getFunctionAddress(function->getName().str()); + return (void *) entryAddr; } void -LLVMLayer::initJit() { - std::string ErrStr; - LLVMInitializeNativeTarget(); - llvm::InitializeNativeTargetAsmPrinter(); - - llvm::EngineBuilder builder(std::unique_ptr(module.release())); - jit.reset(builder - .setEngineKind(llvm::EngineKind::JIT) - .setErrorStr(&ErrStr) - .setVerifyModules(true) - .create() - ); +LLVMLayer::initJit(){ + std::string ErrStr; + llvm::EngineBuilder builder(std::unique_ptr(module.release())); + jit.reset(builder + .setEngineKind(llvm::EngineKind::JIT) + .setErrorStr(&ErrStr) + .setVerifyModules(true) + .create() + ); } void -LLVMLayer::print() { - llvm::PassManager PM; - PM.addPass(llvm::PrintModulePass(llvm::outs(), "banner")); - llvm::AnalysisManager aman; - PM.run(*module.get(), aman); +LLVMLayer::print(){ + llvm::PassManager PM; + PM.addPass(llvm::PrintModulePass(llvm::outs(), "banner")); + llvm::AnalysisManager aman; + PM.run(*module.get(), aman); } void -LLVMLayer::moveToGarbage(void *o) { - __garbage.push_back(o); -} - -llvm::Type* -LLVMLayer::toLLVMType(const ExpandedType& ty) const { - std::map empty; - return toLLVMType(ty, empty); +LLVMLayer::moveToGarbage(void *o){ + __garbage.push_back(o); } llvm::Type* -LLVMLayer::toLLVMType(const ExpandedType& ty, std::map& conjuctions) const { - TypeAnnotation t = ty; - switch (t.__operator) { - case TypeOperator::LIST_ARRAY: - { - assert(t.__operands.size() == 1); - - TypeAnnotation elTy = t.__operands.at(0); - return llvm::ArrayType::get(toLLVMType(ExpandedType(move(elTy)), conjuctions), t.__size); +LLVMLayer::toLLVMType(const ExpandedType &ty) const{ + TypeAnnotation t = ty; + switch(t.__operator){ + case TypeOperator::ARRAY:{ + //see ArrayIR::getRawT() + return nullptr; } - case TypeOperator::LIST_RECORD: - { - std::vector pack_; - pack_.reserve(t.__operands.size()); - - std::transform(t.__operands.begin(), t.__operands.end(), std::inserter(pack_, pack_.end()), - [this, &conjuctions](const TypeAnnotation & t) { - return toLLVMType(ExpandedType(TypeAnnotation(t)), conjuctions); - }); - - llvm::ArrayRef pack(pack_); + case TypeOperator::RECORD:{ + std::vector packVec; + packVec.reserve(t.__operands.size()); - //process recursive types: - if (conjuctions.count(t.conjuctionId)) { - auto result = conjuctions[t.conjuctionId]; - result->setBody(pack, false); - - return result; - } + std::transform(t.__operands.begin(), t.__operands.end(), std::inserter(packVec, packVec.end()), + [this](const TypeAnnotation &t){ + return toLLVMType(ExpandedType(TypeAnnotation(t))); + }); - return llvm::StructType::get(llvmContext, pack, false); + llvm::ArrayRef packArr(packVec); + return llvm::StructType::get(llvmContext, packArr, false); }; - case TypeOperator::LINK: - { - llvm::StructType* conjuction = llvm::StructType::create(llvmContext); - int id = t.conjuctionId; + case TypeOperator::REF:{ + TypeAnnotation tyRef = t.__operands.at(0); + assert(tyRef.__operator == TypeOperator::ALIAS); + llvm::StructType *tyOpaqRaw = llvm::StructType::create(llvmContext, tyRef.__valueCustom); + llvm::PointerType *tyRefRaw = llvm::PointerType::get(tyOpaqRaw, 0); - conjuctions.emplace(id, conjuction); - return conjuction; + return tyRefRaw; }; - case TypeOperator::CALL: - { - assert(false); + case TypeOperator::ALIAS:{ +#ifdef XREATE_ENABLE_EXTERN + //Look in extern types + clang::QualType qt = layerExtern->lookupType(t.__valueCustom); + return layerExtern->toLLVMType(qt); +#else + assert(false); +#endif }; - case TypeOperator::CUSTOM: - { - //Look in extern types - clang::QualType qt = layerExtern->lookupType(t.__valueCustom); - return layerExtern->toLLVMType(qt); - }; - - //DEBT omit ID field in case of single variant. - case TypeOperator::VARIANT: - { - /* Variant Type Layout: - * { - * id :: i8, Holds stored variant id - * storage:: type of biggest variant - * } - */ - uint64_t sizeStorage = 0; - llvm::Type* typStorageRaw = llvm::Type::getVoidTy(llvmContext); - for(const TypeAnnotation& subtype : t.__operands) { - llvm::Type* subtypeRaw = toLLVMType(ExpandedType(subtype), conjuctions); - if (subtypeRaw->isVoidTy()) continue; - - uint64_t sizeSubtype = module->getDataLayout().getTypeStoreSize(subtypeRaw); - if (sizeSubtype > sizeStorage) { - sizeStorage = sizeSubtype; - typStorageRaw = subtypeRaw; - } + //DEBT omit ID field in case of single variant. + case TypeOperator::VARIANT:{ + /* Variant Type Layout: + * { + * id :: i8, Holds stored variant id + * storage:: type of biggest variant + * } + */ + uint64_t sizeStorage = 0; + llvm::Type *typStorageRaw = llvm::Type::getVoidTy(llvmContext); + for(const TypeAnnotation &subtype : t.__operands){ + llvm::Type *subtypeRaw = toLLVMType(ExpandedType(subtype)); + if(subtypeRaw->isVoidTy()) continue; + + uint64_t sizeSubtype = module->getDataLayout().getTypeStoreSize(subtypeRaw); + if(sizeSubtype > sizeStorage){ + sizeStorage = sizeSubtype; + typStorageRaw = subtypeRaw; } + } - std::vector layout; - layout.push_back(llvm::Type::getInt8Ty(llvmContext)); //id + std::vector layout; + layout.push_back(llvm::Type::getInt8Ty(llvmContext)); //id - const bool flagHoldsData = sizeStorage > 0; - if (flagHoldsData) { - layout.push_back(typStorageRaw); //storage - } - - return llvm::StructType::get(llvmContext, llvm::ArrayRef(layout)); - } + const bool flagHoldsData = sizeStorage > 0; + if(flagHoldsData){ + layout.push_back(typStorageRaw); //storage + } - case TypeOperator::NONE: - { - switch (t.__value) { - case TypePrimitive::I32: - case TypePrimitive::Int: - case TypePrimitive::Num: - return llvm::Type::getInt32Ty(llvmContext); + return llvm::StructType::get(llvmContext, llvm::ArrayRef(layout)); + } + case TypeOperator::NONE:{ + switch(t.__value){ case TypePrimitive::Bool: - return llvm::Type::getInt1Ty(llvmContext); + return llvm::Type::getInt1Ty(llvmContext); case TypePrimitive::I8: - return llvm::Type::getInt8Ty(llvmContext); + return llvm::Type::getInt8Ty(llvmContext); + + case TypePrimitive::I32: + return llvm::Type::getInt32Ty(llvmContext); case TypePrimitive::I64: - return llvm::Type::getInt64Ty(llvmContext); + return llvm::Type::getInt64Ty(llvmContext); + + case TypePrimitive::Int: { +// IntBits hintSize; +// if (existsSize(hintSize)){ +// return llvm::IntegerType::getIntNTy(llvmContext, hintSize.n); +// } + + TypesHelper helper(this); + return helper.getPreferredIntTy(); + } case TypePrimitive::Float: - return llvm::Type::getDoubleTy(llvmContext); + return llvm::Type::getDoubleTy(llvmContext); case TypePrimitive::String: - return llvm::Type::getInt8PtrTy(llvmContext); + return llvm::Type::getInt8PtrTy(llvmContext); case TypePrimitive::Invalid: - return llvm::Type::getVoidTy(llvmContext); + return llvm::Type::getVoidTy(llvmContext); default: - assert(false); - } + assert(false); + } } default: - assert(false); - } + assert(false); + } - assert(false); - return nullptr; + assert(false); + return nullptr; } bool -TypeUtils::isStruct(const ExpandedType& ty) { - const TypeAnnotation& t = ty.get(); +TypesHelper::isRecordT(const ExpandedType &ty){ + const TypeAnnotation &t = ty.get(); - if (t.__operator == TypeOperator::LIST_RECORD) { - return true; - } + if(t.__operator == TypeOperator::RECORD){ + return true; + } - if (t.__operator != TypeOperator::CUSTOM) { - return false; - } + if(t.__operator != TypeOperator::ALIAS){ + return false; + } +#ifdef XREATE_ENABLE_EXTERN clang::QualType tqual = llvm->layerExtern->lookupType(t.__valueCustom); const clang::Type * raw = tqual.getTypePtr(); // TODO skip ALL the pointers until non-pointer type found if (raw->isStructureType()) return true; if (!raw->isAnyPointerType()) return false; clang::QualType pointee = raw->getPointeeType(); return pointee->isStructureType(); +#else + assert(false); + return false; +#endif +} + +bool +TypesHelper::isArrayT(const Expanded& ty){ + const TypeAnnotation &t = ty.get(); + + if(t.__operator == TypeOperator::ARRAY){ + return true; + } + + return false; } bool -TypeUtils::isPointer(const ExpandedType &ty) { - if (ty.get().__operator != TypeOperator::CUSTOM) return false; +TypesHelper::isPointerT(const ExpandedType &ty){ + if(ty.get().__operator != TypeOperator::ALIAS) return false; +#ifdef XREATE_ENABLE_EXTERN clang::QualType qt = llvm->layerExtern->lookupType(ty.get().__valueCustom); return llvm->layerExtern->isPointer(qt); +#else + assert(false); + return false; +#endif } + +bool +TypesHelper::isIntegerT(const Expanded& ty){ +return + (ty->__operator == TypeOperator::NONE) && + ((ty->__value == TypePrimitive::Bool) || + (ty->__value == TypePrimitive::I8) || + (ty->__value == TypePrimitive::I32) || + (ty->__value == TypePrimitive::I64) || + (ty->__value == TypePrimitive::Int)); +} + + std::vector -TypeUtils::getStructFields(const ExpandedType &t) { - return (t.get().__operator == TypeOperator::LIST_RECORD) - ? t.get().fields - : llvm->layerExtern->getStructFields( - llvm->layerExtern->lookupType(t.get().__valueCustom)); +TypesHelper::getRecordFields(const ExpandedType &t){ +#ifdef XREATE_ENABLE_EXTERN + return (t.get().__operator == TypeOperator::RECORD) + ? t.get().fields + : llvm->layerExtern->getStructFields( + llvm->layerExtern->lookupType(t.get().__valueCustom)); +#else + assert(t.get().__operator == TypeOperator::RECORD); + return t.get().fields; +#endif +} + +llvm::IntegerType * +TypesHelper::getPreferredIntTy() const{ + unsigned sizePreferred = llvm->module->getDataLayout().getLargestLegalIntTypeSizeInBits(); + return llvm::IntegerType::getIntNTy(llvm->llvmContext, sizePreferred); } \ No newline at end of file diff --git a/cpp/src/llvmlayer.h b/cpp/src/llvmlayer.h index ab6938d..c93fc53 100644 --- a/cpp/src/llvmlayer.h +++ b/cpp/src/llvmlayer.h @@ -1,70 +1,73 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * llvmlayer.h * * Author: pgess */ #ifndef LLVMLAYER_H #define LLVMLAYER_H #include "llvm/IR/Module.h" #include "llvm/IR/Function.h" #include "llvm/IR/PassManager.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Verifier.h" #include "llvm/IR/IRPrintingPasses.h" #include "llvm/IR/IRBuilder.h" #include "llvm/Support/raw_ostream.h" #include "llvm/IR/LLVMContext.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "utils.h" namespace xreate { class AST; class ExternLayer; class TypeAnnotation; /** \brief A wrapper over LLVM toolchain to generate and execute bytecode */ class LLVMLayer { public: - LLVMLayer(AST* rootAST); + LLVMLayer(AST* rootAST); - mutable llvm::LLVMContext llvmContext; - llvm::IRBuilder<> builder; - AST *ast = 0; - ExternLayer *layerExtern =0; - std::unique_ptr module; - std::unique_ptr jit; + mutable llvm::LLVMContext llvmContext; + llvm::IRBuilder<> irBuilder; + AST *ast = 0; + ExternLayer *layerExtern =0; + std::unique_ptr module; + std::unique_ptr jit; + void moveToGarbage(void *o); + llvm::Type* toLLVMType(const Expanded& ty) const; + void print(); + void* getFunctionPointer(llvm::Function* function); - void moveToGarbage(void *o); - - llvm::Type* toLLVMType(const Expanded& ty) const; - void print(); - void* getFunctionPointer(llvm::Function* function); - - void initJit(); + void initJit(); private: - llvm::Type* toLLVMType(const Expanded& ty, std::map& conjunctions) const; - std::vector __garbage; + llvm::Type* toLLVMType(const Expanded& ty, std::map& conjunctions) const; + std::vector __garbage; }; -struct TypeUtils { - bool isStruct(const Expanded& ty); - bool isPointer(const Expanded& ty); - std::vector getStructFields(const Expanded& t); +class TypesHelper { +public: + bool isArrayT(const Expanded& ty); + bool isRecordT(const Expanded& ty); + bool isPointerT(const Expanded& ty); + + bool isIntegerT(const Expanded& ty); + llvm::IntegerType* getPreferredIntTy() const; + + std::vector getRecordFields(const Expanded& t); - TypeUtils(LLVMLayer*llvmlayer) - : llvm(llvmlayer){} + TypesHelper(const LLVMLayer* llvmlayer): llvm(llvmlayer){} - private: - LLVMLayer* llvm; +private: + const LLVMLayer* llvm; }; } #endif // LLVMLAYER_H diff --git a/cpp/src/modules.cpp b/cpp/src/modules.cpp index ca196eb..40fa3c6 100644 --- a/cpp/src/modules.cpp +++ b/cpp/src/modules.cpp @@ -1,168 +1,168 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * modules.cpp * * Author: pgess * Created on July 22, 2017, 5:13 PM */ /** * \file modules.h * \brief Modules support */ #include "modules.h" #include "modules/Parser.h" #include "analysis/utils.h" #include #include #include #include #include using namespace std; namespace fs = boost::filesystem; namespace xreate { namespace modules{ void ModuleRecord::addRequest(const Expression& request){ __requests.push_back(request); } void ModuleRecord::addControllerPath(const std::string& path){ __controllers.push_back(path); } void ModuleRecord::addDiscoveryPath(const std::string& path){ __discoveryPaths.push_back(path); } void ModuleRecord::addProperty(const Expression& prop){ __properties.push_back(prop); } void ModulesSolver::loadControllers(const ModuleRecord& module){ for (const std::string& pathController: module.__controllers){ std::fstream fileContent(pathController); __program << fileContent.rdbuf(); } } void ModulesSolver::extractProperties(const ModuleRecord& module){ - const std::string atomProperty = "bind_module"; - boost::format formatProperty(atomProperty + "(\"%1%\", %2%)."); - - for (const Expression& property: module.__properties){ - std::list reprProp = xreate::analysis::compile(property); - assert(reprProp.size()== 1); - - __program << (formatProperty % module.__path % reprProp.front()) - << std::endl; - } +// const std::string atomProperty = "bind_module"; +// boost::format formatProperty(atomProperty + "(\"%1%\", %2%)."); +// +// for (const Expression& property: module.__properties){ +// std::list reprProp = xreate::analysis::compile(property); +// assert(reprProp.size()== 1); +// +// __program << (formatProperty % module.__path % reprProp.front()) +// << std::endl; +// } } void ModulesSolver::discoverModules(const ModuleRecord& moduleClient){ std::regex extXreate("\\.xreate$", std::regex::basic); for(const std::string& path: moduleClient.__discoveryPaths){ for(fs::directory_entry e: fs::recursive_directory_iterator(path)) { if (fs::is_regular_file(e.status())){ if (!std::regex_search(e.path().string(), extXreate)) continue; FILE* script = fopen(e.path().c_str(), "r"); Scanner scanner(script); Parser parser(&scanner); parser.Parse(); assert(!parser.errors->count && "Discovery errors"); parser.module.__path = e.path().c_str(); extractProperties(parser.module); fclose(script); } } } } void ModulesSolver::extractRequirements(const ModuleRecord& module){ - const std::string atomQuery = "modules_require"; - boost::format formatProperty(atomQuery + "(\"%1%\", %2%)."); - - for (const Expression& query: module.__requests){ - std::list reprQuery = xreate::analysis::compile(query); - assert(reprQuery.size()== 1); - - __program << (formatProperty % module.__path % reprQuery.front()) - << std::endl; - } +// const std::string atomQuery = "modules_require"; +// boost::format formatProperty(atomQuery + "(\"%1%\", %2%)."); +// +// for (const Expression& query: module.__requests){ +// std::list reprQuery = xreate::analysis::compile(query); +// assert(reprQuery.size()== 1); +// +// __program << (formatProperty % module.__path % reprQuery.front()) +// << std::endl; +// } } -void +void ModulesSolver::add(const std::string& base){ __program << base; } void ModulesSolver::init(const std::string& programBase, const ModuleRecord& module){ add(programBase); extractRequirements(module); extractProperties(module); loadControllers(module); discoverModules(module); std::cout << __program.str() << std::endl; } std::list ModulesSolver::run(const ModuleRecord& module){ const std::string predicateResolution = "modules_resolution"; std::map dictResolution; std::vector args{"clingo", nullptr}; DefaultGringoModule moduleDefault; Gringo::Scripts scriptsDefault(moduleDefault); ClingoLib ctl(scriptsDefault, 0, args.data(), {}, 0); ctl.add("base", {}, __program.str()); ctl.ground({{"base", {}}}, nullptr); ctl.solve([&predicateResolution, this, &dictResolution, &module](Gringo::Model const &model) { for (Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) { std::cout << atom << std::endl; if (std::strcmp(atom.name().c_str(), predicateResolution.c_str())!=0) continue; if (atom.arity() == 2){ auto resolution = TranscendLayer::parse(atom); dictResolution.emplace(get<0>(resolution), get<1>(resolution)); continue; } if (atom.arity() == 3){ auto resolution = TranscendLayer::parse(atom); if(get<2>(resolution) != module.__path) continue; dictResolution.emplace(get<0>(resolution), get<1>(resolution)); continue; } assert(false && "Wrong resolution format"); } return true; }, {}); std::list result; for(const Expression& request: module.__requests){ assert(dictResolution.count(request) && "Can't find requested module"); result.push_back(dictResolution.at(request)); } return result; } }} //namespace xreate::modules diff --git a/cpp/src/pass/abstractpass.cpp b/cpp/src/pass/abstractpass.cpp index 26b3195..2cd2d7a 100644 --- a/cpp/src/pass/abstractpass.cpp +++ b/cpp/src/pass/abstractpass.cpp @@ -1,101 +1,100 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess */ /** * \file abstractpass.h * \brief Infrastructure to iterate over AST to facilitate analysis and compilation. */ #include "abstractpass.h" #include "attachments.h" #include "xreatemanager.h" using namespace std; namespace xreate { template<> void defaultValue(){} void IPass::finish(){} IPass::IPass(PassManager *manager) : man(manager) { } template<> void AbstractPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol) { if (__visitedSymbols.isCached(symbol)) return; __visitedSymbols.setCachedValue(symbol); const Expression& declaration = CodeScope::getDefinition(symbol, true); if (declaration.isDefined()){ PassContext context2 = context.updateScope(symbol.scope); process(declaration, context2, hintSymbol); } } template<> void AbstractPass::process(const Expression& expression, PassContext context, const std::string& varDecl){ if (expression.__state == Expression::COMPOUND){ for (const Expression &op: expression.getOperands()) { process(op, context); } for (CodeScope* scope: expression.blocks) { process(scope, context); } if (expression.op == Operator::CALL){ processExpressionCall(expression, context); } return; } if (expression.__state == Expression::IDENT){ assert(context.scope); processSymbol(Attachments::get(expression), context, expression.getValueString()); } } } /** * \class xreate::IPass * * An each pass has to have IPass interface to be controllable by \ref XreateManager. * However in most cases users should inherit minimal useful implementation \ref xreate::AbstractPass * * \note It's usually expected that a custom pass publishes processing results by one of the following means: * - xreate::Attachments for communicating with other passes * - IAnalysisReport to prepare date for the \ref xreate::TranscendLayer solver * * \sa xreate::XreateManager, xreate::AbstractPass */ /** * \class xreate::AbstractPass * * The class traverses the %AST in a minimally useful manner sparing clients from processing an every possible node manually. * This way clients able to focus only on relevant nodes processing. * * The `Output` template parameter specifies the type of node processing result data. * * \note Automatically caches already visited nodes * * \note It's usually expected that a custom pass publishes processing results by one of the following means: * - \ref xreate::Attachments for communicating with other passes * - IAnalysisReport to prepare date for the \ref xreate::TranscendLayer solver * * \sa xreate::XreateManager, xreate::IPass, xreate::AST - */ - + */ \ No newline at end of file diff --git a/cpp/src/pass/abstractpass.h b/cpp/src/pass/abstractpass.h index ea44e23..1d05360 100644 --- a/cpp/src/pass/abstractpass.h +++ b/cpp/src/pass/abstractpass.h @@ -1,206 +1,189 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess */ #ifndef ABSTRACTPASS_H #define ABSTRACTPASS_H -#include "ast.h" +#include "pass/ipass.h" #include "xreatemanager.h" - -#include +#include "ast.h" namespace xreate { /** \brief Holds current position in the %AST during traverse*/ struct PassContext { const CodeScope* scope = 0; ManagedFnPtr function; ManagedRulePtr rule; std::string varDecl; PassContext() {} PassContext updateScope(const CodeScope* scopeNew) { PassContext context2{*this}; context2.scope = scopeNew; return context2; } ~PassContext() {} }; -/** \brief Interface for all passes to inherit. \ref xreate::PassManager holds a collection of passes to execute */ -class IPass { -public: - IPass(PassManager* manager); - - virtual ~IPass() {} - - /** \brief Executes pass */ - virtual void run() = 0; - - /** \brief Finalizes pass. Empty by default*/ - virtual void finish(); - - PassManager* man; -}; - template Output defaultValue(); template<> void defaultValue(); /** \brief Stores processing results for the already visited nodes */ template class SymbolCache : private std::map { public: bool isCached(const Symbol &symbol) { return this->count(symbol); } Output setCachedValue(const Symbol &symbol, Output &&value) { (*this)[symbol] = value; return value; } Output getCachedValue(const Symbol &symbol) { assert(this->count(symbol)); return this->at(symbol); } }; /** \brief Set of the already visited nodes */ template<> class SymbolCache : private std::set { public: bool isCached(const Symbol &symbol) { bool result = this->count(symbol) > 0; return result; } void setCachedValue(const Symbol &symbol) { this->insert(symbol); } void getCachedValue(const Symbol &symbol) { } }; /** \brief Minimal \ref xreate::IPass implementation useful for many passes as the base class*/ template class AbstractPass : public IPass { SymbolCache __visitedSymbols; protected: virtual Output processSymbol(const Symbol &symbol, PassContext context, const std::string &hintSymbol = "") { if(__visitedSymbols.isCached(symbol)) return __visitedSymbols.getCachedValue(symbol); const Expression &declaration = CodeScope::getDefinition(symbol, true); if(declaration.isDefined()) { PassContext context2 = context.updateScope(symbol.scope); Output &&result = process(declaration, context2, hintSymbol); return __visitedSymbols.setCachedValue(symbol, std::move(result)); } return defaultValue(); } Output processExpressionCall(const Expression &expression, PassContext context) { const std::string &calleeName = expression.getValueString(); - std::list callees = man->root->getFunctionSpecializations(calleeName); + std::list callees = man->root->getFnSpecializations(calleeName); //Determined specialization if(callees.size() == 1 && callees.front()) { return processFnCall(callees.front(), context); } //Several specializations or External Fn return processFnCallUncertain(calleeName, callees, context); } SymbolCache &getSymbolCache() { return __visitedSymbols; } public: AbstractPass(PassManager* manager) : IPass(manager) {} /** \brief Processes function invocation instruction */ virtual Output processFnCall(ManagedFnPtr functionCallee, PassContext context) { return defaultValue(); } /** \brief Processes function invocation instruction in uncertain cases * \details Executed when it's impossible statically determine which exactly function is invoked. */ virtual Output processFnCallUncertain(const std::string& calleeName, const std::list& candidates, PassContext context) { return defaultValue(); } /** \brief Processes Logic Rule */ virtual void process(ManagedRulePtr rule) {} /** \brief Processes Function */ virtual Output process(ManagedFnPtr function) { PassContext context; context.function = function; return process(function->getEntryScope(), context); } /** \brief Processes single CodeScope */ virtual Output process(CodeScope* scope, PassContext context, const std::string &hintBlockDecl = "") { context.scope = scope; return processSymbol(Symbol{ScopedSymbol::RetSymbol, scope}, context); } //TODO expose Symbol instead of varDecl. Required by DFAPass. /** \brief Processes single Expression */ virtual Output process(const Expression &expression, PassContext context, const std::string &varDecl = "") { if(expression.__state == Expression::IDENT) { assert(context.scope); return processSymbol(Attachments::get(expression), context, expression.getValueString()); } assert(false); return defaultValue(); } /** \brief Executes AST traverse */ void run() { ManagedRulePtr rule = man->root->begin(); while(rule.isValid()) { process(rule); ++rule; } ManagedFnPtr f = man->root->begin(); while(f.isValid()) { process(f); ++f; } } }; template<> void AbstractPass::processSymbol(const Symbol &symbol, PassContext context, const std::string &hintSymbol); template<> void AbstractPass::process(const Expression &expression, PassContext context, const std::string &hintSymbol); } #endif diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp index 06049f2..65afc4b 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,788 +1,826 @@ /* 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 "ast.h" #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 "compilation/decorators.h" +#include "compilation/pointers.h" #include "analysis/typeinference.h" +#include "compilation/control.h" +#include "compilation/demand.h" +#include "analysis/resources.h" +#ifdef XREATE_ENABLE_EXTERN + #include "ExternLayer.h" +#endif + +#include "compilation/containers.h" +#include "compilation/containers/arrays.h" + +#ifndef XREATE_CONFIG_MIN + #include "query/containers.h" + + #include "pass/versionspass.h" + #include "compilation/targetinterpretation.h" +#endif + #include #include -#include using namespace std; using namespace llvm; namespace xreate{ namespace compilation{ +#define DEFAULT(x) (hintAlias.empty()? x: hintAlias) + std::string -BasicFunctionUnit::prepareName() { - AST* ast = IFunctionUnit::pass->man->root; +BasicBruteFunction::prepareName() { + AST* ast = IBruteFunction::pass->man->root; - string name = ast->getFunctionSpecializations(IFunctionUnit::function->__name).size() > 1 ? - IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : - IFunctionUnit::function->__name; + string name = ast->getFnSpecializations(IBruteFunction::function->__name).size() > 1 ? + IBruteFunction::function->__name + std::to_string(IBruteFunction::function.id()) : + IBruteFunction::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; +BasicBruteFunction::prepareSignature() { + LLVMLayer* llvm = IBruteFunction::pass->man->llvm; + AST* ast = IBruteFunction::pass->man->root; + CodeScope* entry = IBruteFunction::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; +BasicBruteFunction::prepareResult() { + LLVMLayer* llvm = IBruteFunction::pass->man->llvm; + AST* ast = IBruteFunction::pass->man->root; + CodeScope* entry = IBruteFunction::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(); +BasicBruteFunction::prepareBindings() { + CodeScope* entry = IBruteFunction::function->__entry; + IBruteScope* entryCompilation = IBruteFunction::getScopeUnit(entry); + llvm::Function::arg_iterator fargsI = IBruteFunction::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) +IBruteScope::IBruteScope(const CodeScope * const codeScope, IBruteFunction* 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); + args.at(argId), argFormal->getType(), llvm->irBuilder); ++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); + return llvm->irBuilder.CreateCall(__calleeTy, __callee, args, nameStatement); } -//DESABLEDFEATURE implement inlining +llvm::Value* +HiddenArgsFnInvocation::operator() (std::vector&& args, const std::string& hintDecl) { + args.insert(args.end(), __args.begin(), __args.end()); + return __parent->operator ()(std::move(args), hintDecl); +} class CallStatementInline : public IFnInvocation{ public: - CallStatementInline(IFunctionUnit* caller, IFunctionUnit* callee, LLVMLayer* l) + CallStatementInline(IBruteFunction* caller, IBruteFunction* 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; + IBruteFunction* __caller; + IBruteFunction* __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) { } +BasicBruteScope::BasicBruteScope(const CodeScope * const codeScope, IBruteFunction* f, CompilePass* compilePass) +: IBruteScope(codeScope, f, compilePass) { } llvm::Value* -BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { +BasicBruteScope::processSymbol(const Symbol& s, std::string hintRetVar) { Expression declaration = CodeScope::getDefinition(s); const CodeScope* scopeExternal = s.scope; - ICodeScopeUnit* scopeBruteExternal = ICodeScopeUnit::function->getScopeUnit(scopeExternal); + IBruteScope* scopeBruteExternal = IBruteScope::function->getScopeUnit(scopeExternal); assert(scopeBruteExternal->currentBlockRaw); llvm::Value* resultRaw; - llvm::BasicBlock* blockOwn = pass->man->llvm->builder.GetInsertBlock(); + llvm::BasicBlock* blockOwn = pass->man->llvm->irBuilder.GetInsertBlock(); if (scopeBruteExternal->currentBlockRaw == blockOwn) { resultRaw = scopeBruteExternal->process(declaration, hintRetVar); scopeBruteExternal->currentBlockRaw = currentBlockRaw = - pass->man->llvm->builder.GetInsertBlock(); + pass->man->llvm->irBuilder.GetInsertBlock(); } else { - pass->man->llvm->builder.SetInsertPoint(scopeBruteExternal->currentBlockRaw); + pass->man->llvm->irBuilder.SetInsertPoint(scopeBruteExternal->currentBlockRaw); resultRaw = scopeBruteExternal->processSymbol(s, hintRetVar); - pass->man->llvm->builder.SetInsertPoint(blockOwn); + pass->man->llvm->irBuilder.SetInsertPoint(blockOwn); } return resultRaw; } IFnInvocation* -BasicCodeScopeUnit::findFunction(const Expression& opCall) { +BasicBruteScope::findFunction(const Expression& opCall) { const std::string& calleeName = opCall.getValueString(); LLVMLayer* llvm = pass->man->llvm; - const std::list& specializations = pass->man->root->getFunctionSpecializations(calleeName); + const std::list& specializations = pass->man->root->getFnSpecializations(calleeName); - //if no specializations registered - check external function - if (specializations.size() == 0) { - llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName); +#ifdef XREATE_ENABLE_EXTERN + //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"; + llvm::outs() << "Debug/External function: " << calleeName; + external->getType()->print(llvm::outs(), true); + llvm::outs() << "\n"; - return new BruteFnInvocation(external, llvm); - } + return new BruteFnInvocation(external, llvm); + } +#endif //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; +BasicBruteScope::process(const Expression& expr, const std::string& hintAlias, const TypeAnnotation& expectedT) { + llvm::Value *leftRaw; + llvm::Value *rightRaw; LLVMLayer& l = *pass->man->llvm; - xreate::compilation::AdvancedInstructions instructions = xreate::compilation::AdvancedInstructions({this, function, pass}); + Context ctx{this, function, pass}; + xreate::compilation::ControlIR controlIR = xreate::compilation::ControlIR({this, function, pass}); switch (expr.op) { + case Operator::ADD: 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]); + leftRaw = process(expr.operands[0]); + rightRaw = 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; + return l.irBuilder.CreateAdd(leftRaw, rightRaw, DEFAULT("addv")); } case Operator::SUB: - return l.builder.CreateSub(left, right, DEFAULT("tmp_sub")); + return l.irBuilder.CreateSub(leftRaw, rightRaw, DEFAULT("tmp_sub")); break; case Operator::MUL: - return l.builder.CreateMul(left, right, DEFAULT("tmp_mul")); + return l.irBuilder.CreateMul(leftRaw, rightRaw, 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")); + if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateSDiv(leftRaw, rightRaw, DEFAULT("tmp_div")); + if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFDiv(leftRaw, rightRaw, 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")); + if (leftRaw->getType()->isIntegerTy()) return l.irBuilder.CreateICmpEQ(leftRaw, rightRaw, DEFAULT("tmp_equ")); + if (leftRaw->getType()->isFloatingPointTy()) return l.irBuilder.CreateFCmpOEQ(leftRaw, rightRaw, DEFAULT("tmp_equ")); const ExpandedType& leftT = pass->man->root->getType(expr.operands[0]); - const ExpandedType& rightT = pass->man->root->getType(expr.operands[0]); + const ExpandedType& rightT = pass->man->root->getType(expr.operands[1]); 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")); + llvm::Type* selectorT = llvm::cast(leftRaw->getType())->getElementType(0); + llvm::Value* leftUnwapped = typeinference::doAutomaticTypeConversion(leftRaw, selectorT, l.irBuilder); + llvm::Value* rightUnwapped = typeinference::doAutomaticTypeConversion(rightRaw, selectorT, l.irBuilder); + return l.irBuilder.CreateICmpEQ(leftUnwapped, rightUnwapped, DEFAULT("tmp_equ")); } - - break; } case Operator::NE: - return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne")); + return l.irBuilder.CreateICmpNE(leftRaw, rightRaw, DEFAULT("tmp_ne")); break; case Operator::LSS: - return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss")); + return l.irBuilder.CreateICmpSLT(leftRaw, rightRaw, DEFAULT("tmp_lss")); break; case Operator::LSE: - return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse")); + return l.irBuilder.CreateICmpSLE(leftRaw, rightRaw, DEFAULT("tmp_lse")); break; case Operator::GTR: - return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr")); + return l.irBuilder.CreateICmpSGT(leftRaw, rightRaw, DEFAULT("tmp_gtr")); break; case Operator::GTE: - return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte")); + return l.irBuilder.CreateICmpSGE(leftRaw, rightRaw, DEFAULT("tmp_gte")); break; case Operator::NEG: { - left = process(expr.operands[0]); + leftRaw = process(expr.operands[0]); ExpandedType leftTy = pass->man->root->getType(expr.operands[0]); if (leftTy->__value == TypePrimitive::Bool){ - return l.builder.CreateNot(left, DEFAULT("tmp_not")); + return l.irBuilder.CreateNot(leftRaw, DEFAULT("tmp_not")); } else { - return l.builder.CreateNeg(left, DEFAULT("tmp_neg")); + return l.irBuilder.CreateNeg(leftRaw, 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")); + return controlIR.compileIf(expr, DEFAULT("tmp_if")); } case Operator::SWITCH: { - return instructions.compileSwitch(expr, DEFAULT("tmp_switch")); + return controlIR.compileSwitch(expr, DEFAULT("tmp_switch")); } case Operator::LOGIC_AND: { assert(expr.operands.size() == 1); return process(expr.operands[0]); } - case Operator::LIST: + case Operator::LIST: //init record or array { - 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); + ExpandedType exprT = l.ast->getType(expr, expectedT); + TypesHelper helper(pass->man->llvm); + + enum {RECORD, ARRAY} kind; + if (helper.isArrayT(exprT)){ + kind = ARRAY; + + } else if (helper.isRecordT(exprT)){ + kind = RECORD; + } else { + assert(false && "Inapproriate type"); + } + + #ifdef XREATE_ENABLE_EXTERN + if (exprT->__operator == TypeOperator::ALIAS){ + if (l.layerExtern->isArrayType(exprT->__valueCustom)){ + flagIsArray = true; + break; + } + + if (l.layerExtern->isRecordType(exprT->__valueCustom)){ + flagIsArray = false; + break; + } + + assert(false && "Inapproriate external type"); + } + #endif + + switch(kind){ + case RECORD:{ + const std::vector fieldsFormal = helper.getRecordFields(exprT); + containers::RecordIR irRecords(ctx); + llvm::StructType *recordTRaw = llvm::cast(l.toLLVMType(exprT)); + llvm::Value *resultRaw = irRecords.init(recordTRaw); + return irRecords.update(resultRaw, exprT, expr); } - 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})); + case ARRAY: { + std::unique_ptr containerIR( + containers::IContainersIR::create(expr, expectedT, ctx)); + llvm::Value* aggrRaw = containerIR->init(hintAlias); + return containerIR->update(aggrRaw, expr, hintAlias); } - - return record; + } + break; }; 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")); + return controlIR.compileMapSolidOutput(expr, DEFAULT("map")); }; case Operator::FOLD: { - return instructions.compileFold(expr, DEFAULT("fold")); + return controlIR.compileFold(expr, DEFAULT("fold")); }; - case Operator::INF: + case Operator::FOLD_INF: { - return instructions.compileFoldInf(expr, DEFAULT("fold")); + return controlIR.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]); + assert(expr.operands.size() > 1); + + const Expression& aggrE = expr.operands[0]; + const ExpandedType& aggrT = pass->man->root->getType(aggrE); + llvm::Value* aggrRaw = process(aggrE); + switch (aggrT->__operator) { + case TypeOperator::RECORD: + { + list fieldsList; + for(auto opIt = ++expr.operands.begin(); opIt!=expr.operands.end(); ++opIt){ + fieldsList.push_back(getIndexStr(*opIt)); + } - llvm::Value* aggr = processSymbol(s, hintIdent); + return controlIR.compileStructIndex(aggrRaw, aggrT, fieldsList); + }; - 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"); + case TypeOperator::ARRAY: + { + std::vector indexes; + std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()), + [this] (const Expression & op) { + return process(op); } + ); - return instructions.compileStructIndex(aggr, t2, idxField); - }; + std::unique_ptr containersIR( + containers::IContainersIR::create(aggrE, expectedT, ctx) + ); - 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)); - }; + containers::ArrayIR* arraysIR = static_cast(containersIR.get()); + return arraysIR->get(aggrRaw, indexes, hintAlias); + }; - default: - assert(false); - } + 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); - } +// 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.irBuilder.CreateLoad(storage, hintAlias); +// } 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; + const ExpandedType& typResult = pass->man->root->getType(expr); + llvm::Type* typResultRaw = l.toLLVMType(typResult); + llvm::Type* typIdRaw = llvm::cast(typResultRaw)->getElementType(0); + + uint64_t id = expr.getValueDouble(); + llvm::Value* resultRaw = llvm::UndefValue::get(typResultRaw); + resultRaw = l.irBuilder.CreateInsertValue(resultRaw, llvm::ConstantInt::get(typIdRaw, id), llvm::ArrayRef({0})); + + const ExpandedType& typVariant = ExpandedType(typResult->__operands.at(id)); + llvm::Type* typVariantRaw = l.toLLVMType(typVariant); + llvm::Value* variantRaw = llvm::UndefValue::get(typVariantRaw); + assert(expr.operands.size() == typVariant->__operands.size() && "Wrong variant arguments count"); + if (!typVariant->__operands.size()) return resultRaw; + + for (unsigned int fieldId = 0; fieldId < expr.operands.size(); ++fieldId) { + const ExpandedType& typField = ExpandedType(typVariant->__operands.at(fieldId)); + Attachments::put(expr.operands.at(fieldId), typField); + llvm::Value* fieldRaw = process(expr.operands.at(fieldId)); + assert(fieldRaw); + + variantRaw = l.irBuilder.CreateInsertValue(variantRaw, fieldRaw, llvm::ArrayRef({fieldId})); + } + + llvm::Type* typStorageRaw = llvm::cast(typResultRaw)->getElementType(1); + llvm::Value* addrAsStorage = l.irBuilder.CreateAlloca(typStorageRaw); + llvm::Value* addrAsVariant = l.irBuilder.CreateBitOrPointerCast(addrAsStorage, typVariantRaw->getPointerTo()); + + l.irBuilder.CreateStore(variantRaw, addrAsVariant); + llvm::Value* storageRaw = l.irBuilder.CreateLoad(typStorageRaw, addrAsStorage); + resultRaw = l.irBuilder.CreateInsertValue(resultRaw, storageRaw, llvm::ArrayRef({1})); + + return resultRaw; } case Operator::SWITCH_VARIANT: { - return instructions.compileSwitchVariant(expr, DEFAULT("tmpswitch")); + return controlIR.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); + return controlIR.compileSequence(expr); } case Operator::UNDEF: { - llvm::Type* typExprUndef = l.toLLVMType(typeinference::getType(expr, *pass->man->root)); - return llvm::UndefValue::get(typExprUndef); + llvm::Type* typExprUndef = l.toLLVMType(pass->man->root->getType(expr, expectedT)); + return llvm::UndefValue::get(typExprUndef); + } + + case Operator::UPDATE: + { + TypesHelper helper(pass->man->llvm); + containers::RecordIR irRecords(ctx); + + const Expression& aggrE = expr.operands.at(0); + const Expression& updE = expr.operands.at(1); + const ExpandedType& aggrT = pass->man->root->getType(aggrE); + llvm::Value* aggrRaw = process(aggrE); + + if (helper.isRecordT(aggrT)){ + return irRecords.update(aggrRaw, aggrT, updE); + } + + if (helper.isArrayT(aggrT)){ + if (updE.op == Operator::LIST_INDEX){ + + std::unique_ptr containersIR( + containers::IContainersIR::create(aggrE, TypeAnnotation(), ctx + )); + + return containersIR->update(aggrRaw, updE, hintAlias); + } + } + + assert(false); + return nullptr; } 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)); + llvm::Type* typConst = l.toLLVMType(pass->man->root->getType(expr, expectedT)); 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")); + return controlIR.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) { +BasicBruteScope::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); + pass->man->llvm->irBuilder.SetInsertPoint(block); } - currentBlockRaw = pass->man->llvm->builder.GetInsertBlock(); + currentBlockRaw = pass->man->llvm->irBuilder.GetInsertBlock(); Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope}; return processSymbol(symbScope); } -ICodeScopeUnit::~ICodeScopeUnit() { } +IBruteScope::~IBruteScope() { } -IFunctionUnit::~IFunctionUnit() { } +IBruteFunction::~IBruteFunction() { } llvm::Function* -IFunctionUnit::compile() { +IBruteFunction::compile() { if (raw != nullptr) return raw; LLVMLayer* llvm = pass->man->llvm; - llvm::IRBuilder<>& builder = llvm->builder; + llvm::IRBuilder<>& builder = llvm->irBuilder; 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)); + builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->irBuilder)); if (blockCurrent) { builder.SetInsertPoint(blockCurrent); } llvm->moveToGarbage(ft); return raw; } -ICodeScopeUnit* -IFunctionUnit::getScopeUnit(const CodeScope * const scope) { +IBruteScope* +IBruteFunction::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)); + 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) { +IBruteScope* +IBruteFunction::getScopeUnit(ManagedScpPtr scope) { return getScopeUnit(&*scope); } -ICodeScopeUnit* -IFunctionUnit::getEntry() { +IBruteScope* +IBruteFunction::getEntry() { return getScopeUnit(function->getEntryScope()); } template<> -compilation::IFunctionUnit* +compilation::IBruteFunction* CompilePassCustomDecorators ::buildFunctionUnit(const ManagedFnPtr& function) { return new BruteFunctionDefault(function, this); } template<> -compilation::ICodeScopeUnit* +compilation::IBruteScope* CompilePassCustomDecorators -::buildCodeScopeUnit(const CodeScope * const scope, IFunctionUnit* function) { +::buildCodeScopeUnit(const CodeScope * const scope, IBruteFunction* function) { return new DefaultCodeScopeUnit(scope, function, this); } +std::string +BasicBruteScope::getIndexStr(const Expression& index){ + switch(index.__state){ + +//named struct field + case Expression::STRING: + return index.getValueString(); + break; + +//anonymous struct field + case Expression::NUMBER: + return to_string((int) index.getValueDouble()); + break; + + default: + assert(false && "Wrong index for a struct"); + } + return ""; +} + } // end of compilation -compilation::IFunctionUnit* +compilation::IBruteFunction* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { - compilation::IFunctionUnit* unit = buildFunctionUnit(function); + compilation::IBruteFunction* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } +void +CompilePass::prepare(){ + //Initialization: +#ifndef XREATE_CONFIG_MIN +#endif + managerTransformations = new xreate::compilation::TransformationsManager(); + targetInterpretation = new interpretation::TargetInterpretation(man, this); +} + void CompilePass::run() { - //Initialization: - managerTransformations = new xreate::compilation::TransformationsManager(); - targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); + prepare(); - //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"); + //Determine entry function: + StaticModel model = man->transcend->query(analysis::FN_ENTRY_PREDICATE); + 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)); + string nameMain = std::get<0>(TranscendLayer::parse(model.begin()->second)); + compilation::IBruteFunction* unitMain = getFunctionUnit(man->root->findFunction(nameMain)); - //Compilation itself: - entry = unitMain->compile(); + //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); +#ifndef XREATE_CONFIG_MIN + transcend->registerQuery(new latex::LatexQuery(), QueryId::LatexQuery); +#endif + + transcend->registerQuery(new containers::Query(), QueryId::ContainersQuery); + transcend->registerQuery(new demand::DemandQuery(), QueryId::DemandQuery); + transcend->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery); } + } //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 + * - %Function Decorators to alter function-level compilation. See \ref xreate::compilation::IBruteFunction * - 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/src/pass/compilepass.h b/cpp/src/pass/compilepass.h index dc4aba2..bc503c1 100644 --- a/cpp/src/pass/compilepass.h +++ b/cpp/src/pass/compilepass.h @@ -1,210 +1,225 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * compilepass.h */ #ifndef COMPILEPASS_H #define COMPILEPASS_H #include "abstractpass.h" #include "llvm/IR/Function.h" namespace xreate { class TranscendLayer; class CompilePass; class LLVMLayer; namespace interpretation{ class TargetInterpretation; } } namespace xreate { namespace compilation { -class ICodeScopeUnit; -class IFunctionUnit; +class IBruteScope; +class IBruteFunction; class TransformationsManager; /** \brief Holds current position in %AST while traversing*/ struct Context{ - ICodeScopeUnit* scope; - IFunctionUnit* function; + IBruteScope* scope; + IBruteFunction* function; CompilePass* pass; }; /** \brief Interface for custom function invocation operation compilation * \details Default implementation is xreate::compilation::BruteFnInvocation */ class IFnInvocation { public: /** \brief Returns result of custom function invocation for the given arguments*/ virtual llvm::Value* operator() (std::vector&& args, const std::string& hintDecl="") = 0; }; /** \brief Default IFnInvocation implementation */ class BruteFnInvocation: public IFnInvocation{ public: BruteFnInvocation(llvm::Function* callee, LLVMLayer* l) : __callee(callee), __calleeTy(callee->getFunctionType()), llvm(l) {} BruteFnInvocation(llvm::Value* callee, llvm::FunctionType* ty, LLVMLayer* l) : __callee(callee), __calleeTy(ty), llvm(l) {} /** \brief Makes type conversions and returns LLVM call statement with given arguments*/ llvm::Value* operator() (std::vector&& args, const std::string& hintDecl=""); protected: llvm::Value* __callee; llvm::FunctionType* __calleeTy; LLVMLayer* llvm; }; +/** \brief %Function invocation operator decorator to handle latex enabled functions with hidden extra arguments */ +class HiddenArgsFnInvocation : public compilation::IFnInvocation{ +public: + HiddenArgsFnInvocation(std::vector args, compilation::IFnInvocation *parent) + : __args(args), __parent(parent){} + + llvm::Value *operator()(std::vector &&args, const std::string &hintDecl = ""); + +private: + std::vector __args; + compilation::IFnInvocation *__parent; +}; + /** \brief Interface to allow modification of CodeScope compilation * \details Default implementation defined in xreate::compilation::DefaultCodeScopeUnit */ -class ICodeScopeUnit{ +class IBruteScope{ public: - CompilePass* const pass; - IFunctionUnit* const function; - const CodeScope* const scope; - llvm::BasicBlock* currentBlockRaw; - - ICodeScopeUnit(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass); - virtual ~ICodeScopeUnit(); + CompilePass* const pass; + IBruteFunction* const function; + const CodeScope* const scope; + llvm::BasicBlock* currentBlockRaw; + + IBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass); + virtual ~IBruteScope(); + + virtual llvm::Value* compile(const std::string& hintBlockDecl="")=0; + virtual llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="")=0; + virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="", const TypeAnnotation& expectedT = TypeAnnotation())=0; + + virtual Symbol bindArg(llvm::Value* value, std::string&& alias)=0; + virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0; + virtual void reset() = 0; - virtual llvm::Value* compile(const std::string& hintBlockDecl="")=0; - virtual llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="")=0; - virtual llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="")=0; - - virtual Symbol bindArg(llvm::Value* value, std::string&& alias)=0; - virtual void bindArg(llvm::Value* value, const ScopedSymbol& s)=0; - virtual void reset() = 0; protected: - - /** \brief For subclasses to implement this method to define a function name resolution*/ - virtual IFnInvocation* findFunction(const Expression& opCall)=0; + /** \brief For subclasses to implement this method to define a function name resolution*/ + virtual IFnInvocation* findFunction(const Expression& opCall)=0; }; -/** \brief Minimal useful ICodeScopeUnit implementation suited for inheritance */ -class BasicCodeScopeUnit: public ICodeScopeUnit{ +/** \brief Minimal useful IBruteScope implementation suited for inheritance */ +class BasicBruteScope: public IBruteScope{ public: - BasicCodeScopeUnit(const CodeScope* const codeScope, IFunctionUnit* f, CompilePass* compilePass); + BasicBruteScope(const CodeScope* const codeScope, IBruteFunction* f, CompilePass* compilePass); llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar="") override; - llvm::Value* process(const Expression& expr, const std::string& hintVarDecl="") override; + llvm::Value* process(const Expression& expr, const std::string& hintAlias="", const TypeAnnotation& expectedT = TypeAnnotation()) override; llvm::Value* compile(const std::string& hintBlockDecl="") override; protected: IFnInvocation* findFunction(const Expression& opCall) override; + +private: + std::string getIndexStr(const Expression& index); }; /** \brief Interface to specify compilation of %Function */ -class IFunctionUnit{ +class IBruteFunction{ public: - IFunctionUnit(ManagedFnPtr f, CompilePass* p): function(f), pass(p) {} - virtual ~IFunctionUnit(); + IBruteFunction(ManagedFnPtr f, CompilePass* p): function(f), pass(p) {} + virtual ~IBruteFunction(); llvm::Function* compile(); - ICodeScopeUnit* getEntry(); - ICodeScopeUnit* getScopeUnit(const CodeScope * const scope); - ICodeScopeUnit* getScopeUnit(ManagedScpPtr scope); + IBruteScope* getEntry(); + IBruteScope* getScopeUnit(const CodeScope * const scope); + IBruteScope* getScopeUnit(ManagedScpPtr scope); virtual llvm::Type* prepareResult() = 0; ManagedFnPtr function; llvm::Function* raw = nullptr; protected: CompilePass* pass=nullptr; virtual std::string prepareName() = 0; virtual std::vector prepareSignature() = 0; virtual llvm::Function::arg_iterator prepareBindings() = 0; private: - std::map> __scopes; - std::list> __orphanedScopes; + std::map> __scopes; + std::list> __orphanedScopes; }; -/** \brief Minimal useful IFunctionUnit implementation suited for inheritance */ -class BasicFunctionUnit: public IFunctionUnit{ +/** \brief Minimal useful IBruteFunction implementation suited for inheritance */ +class BasicBruteFunction: public IBruteFunction{ public: - BasicFunctionUnit(ManagedFnPtr f, CompilePass* p) - : IFunctionUnit(f, p) {} + BasicBruteFunction(ManagedFnPtr f, CompilePass* p) + : IBruteFunction(f, p) {} protected: std::string prepareName() override; virtual std::vector prepareSignature() override; virtual llvm::Type* prepareResult() override; virtual llvm::Function::arg_iterator prepareBindings() override; }; } // end of namespace compilation class CompilePass : public AbstractPass { - friend class compilation::BasicCodeScopeUnit; - friend class compilation::IFunctionUnit; + friend class compilation::BasicBruteScope; + friend class compilation::IBruteFunction; public: compilation::TransformationsManager* managerTransformations; interpretation::TargetInterpretation* targetInterpretation; CompilePass(PassManager* manager): AbstractPass(manager) {} /** \brief Executes compilation process */ void run() override; /**\brief Returns compiled specified %Function * \details Executes function compilation or read cache if it's already done */ - compilation::IFunctionUnit* getFunctionUnit(const ManagedFnPtr& function); + compilation::IBruteFunction* getFunctionUnit(const ManagedFnPtr& function); /**\brief Returns compiled main(entry) %Function in program */ llvm::Function* getEntryFunction(); /** \brief Initializes queries required by compiler. See xreate::IQuery, xreate::TranscendLayer */ static void prepareQueries(TranscendLayer* transcend); - - - + void prepare(); + protected: - virtual compilation::IFunctionUnit* buildFunctionUnit(const ManagedFnPtr& function)=0; - virtual compilation::ICodeScopeUnit* buildCodeScopeUnit(const CodeScope* const scope, compilation::IFunctionUnit* function)=0; + virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function)=0; + virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, compilation::IBruteFunction* function)=0; private: //TODO free `functions` in destructor - std::map functions; + std::map functions; llvm::Function* entry = 0; }; namespace compilation{ /** \brief Constructs compiler with desired %Function and %Code Scope decorators. See adaptability in xreate::CompilePass*/ template class CompilePassCustomDecorators: public ::xreate::CompilePass{ public: CompilePassCustomDecorators(PassManager* manager): ::xreate::CompilePass(manager) {} - virtual compilation::IFunctionUnit* buildFunctionUnit(const ManagedFnPtr& function) override{ + virtual compilation::IBruteFunction* buildFunctionUnit(const ManagedFnPtr& function) override{ return new FUNCTION_DECORATOR(function, this); } - virtual compilation::ICodeScopeUnit* buildCodeScopeUnit(const CodeScope* const scope, IFunctionUnit* function) override{ + virtual compilation::IBruteScope* buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function) override{ return new SCOPE_DECORATOR(scope, function, this); } }; template<> -compilation::IFunctionUnit* +compilation::IBruteFunction* CompilePassCustomDecorators::buildFunctionUnit(const ManagedFnPtr& function); template<> -compilation::ICodeScopeUnit* -CompilePassCustomDecorators::buildCodeScopeUnit(const CodeScope* const scope, IFunctionUnit* function); +compilation::IBruteScope* +CompilePassCustomDecorators::buildCodeScopeUnit(const CodeScope* const scope, IBruteFunction* function); }} //end of namespace xreate::compilation #endif // COMPILEPASS_H diff --git a/cpp/src/pass/dfapass.cpp b/cpp/src/pass/dfapass.cpp index e4adedc..d4da4b1 100644 --- a/cpp/src/pass/dfapass.cpp +++ b/cpp/src/pass/dfapass.cpp @@ -1,266 +1,266 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * dfapass.cpp */ /** * \file dfapass.h * \brief Data Flow Analysis */ //DEBT DFA represent VersionaPass in declarative form using applyDependencies // applyDependencies(expression, context, cache, decl); //DEBT DFA prepare static annotations and represent InterpretationPass in declarative form // applyStaticAnnotations(expression, context, cache, decl); //DEBT DFA Eliminate dfa schemes #include "pass/dfapass.h" #include "xreatemanager.h" #include "transcendlayer.h" #include #include using namespace std; namespace xreate {namespace dfa { DFAPass::DFAPass(PassManager* manager) : AbstractPass(manager) , graph{new DFAGraph()} , transcend(manager->transcend) { } void DFAPass::processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result) { const string &nameCalleeFunction=expr.getValueString(); //TODO implement processFnCall/Uncertain list variantsCalleeFunction=man->root->getFunctionSpecializations(nameCalleeFunction); vector operands; operands.reserve(expr.getOperands().size()); for(const Expression& arg : expr.getOperands()) { operands.push_back(process(arg, context)); } //Set calling relations: DFACallInstanceType type=variantsCalleeFunction.size()>1?WEAK:STRONG; for(ManagedFnPtr function : variantsCalleeFunction) { CodeScope *scopeRemote=function->getEntryScope(); DFACallInstance callInstance; callInstance.fnName=function->getName(); callInstance.type=type; std::vector::const_iterator nodeActual=operands.begin(); for(const std::string &identFormal : scopeRemote->__bindings) { const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE}; SymbolPacked symbolFormalPacked=transcend->pack(Symbol{symbolFormal, scopeRemote}, nameCalleeFunction+":"+identFormal); callInstance.args.push_back(std::make_pair(symbolFormalPacked, *nodeActual)); ++nodeActual; } callInstance.retActual=result; SymbolNode retFormal=SymbolNode(transcend->pack(Symbol{ScopedSymbol::RetSymbol, scopeRemote}, nameCalleeFunction+":[ret]")); graph->addCallInstance(std::move(callInstance)); } } void DFAPass::processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache) { cache.operands.reserve(expression.getOperands().size()); for(const Expression &op : expression.getOperands()) { const SymbolNode& subnodeOperand=process(op, context); cache.operands.push_back(subnodeOperand); graph->addDependency(node, subnodeOperand); } cache.blocks.reserve(expression.blocks.size()); for(CodeScope* block : expression.blocks) { const SymbolNode& subnodeBlock=process(block, context); cache.blocks.push_back(subnodeBlock); graph->addDependency(node, subnodeBlock); } } void DFAPass::processAnnotations(const Expression& expression, PassContext context, const SymbolNode& ident){ for (const pair& tag : expression.tags){ graph->printInplaceAnnotation(ident, tag.second); } } SymbolNode DFAPass::process(const Expression& expression, PassContext context, const std::string& varDecl) { SymbolNode result; - if(Attachments::exists(expression)){ - Symbol varSymbol=Attachments::get(expression); + if(Attachments::exists(expression)){ + Symbol varSymbol=Attachments::get(expression); result=transcend->pack(varSymbol, varDecl); } else if(expression.__state==Expression::IDENT&&expression.tags.size()==0){ Symbol varSymbol=Attachments::get(expression); result=transcend->pack(varSymbol, expression.getValueString()); } else { result=SymbolAnonymous{expression.id}; } processAnnotations(expression, context, result); ProcessingCache cache; switch(expression.__state) { case Expression::COMPOUND: { switch(expression.op) { case Operator::CALL: { processCallInstance(expression, context, result); break; } case Operator::IF: { const SymbolNode& scopeA=process(expression.blocks.front(), context, "ifTrue" + std::to_string(expression.id)); const SymbolNode& scopeB=process(expression.blocks.back(), context, "ifFalse" + std::to_string(expression.id)); const SymbolNode& condition=process(expression.operands.at(0), context); graph->addDependency(result, scopeA); graph->addDependency(result, scopeB); graph->addDependency(result, condition); graph->printWeakAlias(result, scopeA); graph->printWeakAlias(result, scopeB); break; } case Operator::SWITCH: case Operator::SWITCH_VARIANT: { for(CodeScope* block : expression.blocks) { const SymbolNode& subnodeBlock=process(block, context, "case"+to_string(block->getBody().id)); graph->addDependency(result, subnodeBlock); graph->printWeakAlias(result, subnodeBlock); } const SymbolNode& condition=process(expression.operands.at(0), context); graph->addDependency(result, condition); break; } case Operator::MAP: processDependencies(result, expression, context, cache); graph->printOperator(Operator::MAP, {result, cache.operands.at(0), cache.blocks.at(0)}); break; case Operator::FOLD: processDependencies(result, expression, context, cache); graph->printOperator(Operator::FOLD, {result, cache.operands.at(0), cache.operands.at(1), cache.blocks.at(0)}); break; case Operator::LIST: processDependencies(result, expression, context, cache); graph->printOperator(Operator::LIST, {result}, expression.getOperands().size()); break; case Operator::LIST_RANGE: processDependencies(result, expression, context, cache); graph->printOperator(Operator::LIST_RANGE, {result}); break; case Operator::INDEX: processDependencies(result, expression, context, cache); graph->printOperator(Operator::INDEX, {result, cache.operands.at(0), cache.operands.at(1)}); break; default: { processDependencies(result, expression, context, cache); break; } } break; } case Expression::IDENT: { SymbolNode symbIdent=AbstractPass::process(expression, context, varDecl); if(!(result==symbIdent)){ graph->addDependency(result, symbIdent); graph->printAlias(result, symbIdent); } break; } case Expression::NUMBER: case Expression::STRING: { break; } case Expression::INVALID: case Expression::BINDING: { assert(false); break; } } return result; } SymbolNode DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl) { if (!hintBlockDecl.empty()) { Symbol symbRet{ScopedSymbol::RetSymbol, scope}; transcend->pack(symbRet, hintBlockDecl + ":[ret]"); } for(const std::string& binding : scope->__bindings) { Symbol bindingSymb{scope->getSymbol(binding), scope}; SymbolPacked bindingSymbP=transcend->pack(bindingSymb, binding); getSymbolCache().setCachedValue(bindingSymb, SymbolNode(bindingSymbP)); processAnnotations(scope->getDefinition(bindingSymb), context, SymbolNode(bindingSymbP)); } return AbstractPass::process(scope, context, hintBlockDecl); } SymbolNode DFAPass::process(ManagedFnPtr function) { transcend->pack(Symbol{ScopedSymbol::RetSymbol, function->getEntryScope()}, function->getName()+to_string(function.id())+":[ret]"); SymbolNode result=AbstractPass::process(function); graph->printFunctionRet(function, result); return result; } void DFAPass::finish() { transcend->registerReport(graph); //Declare symbols: graph->printSymbols(transcend); AbstractPass::finish(); } } //end of namespace dfa template<> SymbolNode defaultValue() { assert(false); } } //end of xreate namespace /** * \class xreate::dfa::DFAPass * \details Provides DFA, an important analysis for the reasoning. Traverses over AST and stores the collected data in the \ref xreate::dfa::DFAGraph */ diff --git a/cpp/src/pass/interpretationpass.cpp b/cpp/src/pass/interpretationpass.cpp index e4b1265..5f1d29e 100644 --- a/cpp/src/pass/interpretationpass.cpp +++ b/cpp/src/pass/interpretationpass.cpp @@ -1,554 +1,563 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: interpretationpass.cpp * Author: pgess * * Created on July 5, 2016, 5:21 PM */ /** * \file interpretationpass.h * \brief Interpretation analysis: determines what parts of a code could be interpreted */ +#include "ast.h" #include "pass/interpretationpass.h" -#include +#include "compilation/targetinterpretation.h" +#include "analysis/utils.h" +#include "analysis/predefinedanns.h" + #include -#include "ast.h" + //DEBT implement InterpretationPass purely in transcend //DEBT represent InterpretationPass as general type inference using namespace std; namespace xreate { template<> interpretation::InterpretationResolution defaultValue() { return interpretation::CMPL_ONLY; } namespace interpretation { enum InterpretationQuery { QUERY_INTR_ONLY, QUERY_CMPL_ONLY }; namespace details { template bool checkConstraints(InterpretationResolution flag) { return( (flag==INTR_ONLY&&FLAG_REQUIRED==QUERY_INTR_ONLY) ||(flag==CMPL_ONLY&&FLAG_REQUIRED==QUERY_CMPL_ONLY)); } InterpretationResolution recognizeTags(const map& tags) { - auto i=tags.find("i12n"); - if(i==tags.end()){ - return ANY; - } - - assert(i->second.op==Operator::CALL); - const string& cmd=i->second.operands.at(0).getValueString(); - - //TODO make consistent names of annotation and resolution - if(cmd=="on"){ - return INTR_ONLY; - - } else if(cmd=="off"){ - return CMPL_ONLY; - } - - return ANY; + std::list tagsL; + auto predefined = analysis::PredefinedAnns::instance(); + for(const auto& tag: tags){tagsL.push_back(tag.second);} + const Expression& tagI12nE = analysis::findAnnById( + (unsigned) analysis::PredefinedAnns::ExprAnnotations::I12N, + ExpandedType(predefined.exprAnnsT), + tagsL); + + if (!tagI12nE.isValid()) return ANY; + analysis::PredefinedAnns::I12ModeTag modeI12n = (analysis::PredefinedAnns::I12ModeTag) tagI12nE.operands.at(0).getValueDouble(); + + switch(modeI12n){ + case analysis::PredefinedAnns::I12ModeTag::ON: + return INTR_ONLY; + case analysis::PredefinedAnns::I12ModeTag::OFF: + return CMPL_ONLY; + } + + return ANY; } } InterpretationResolution unify(InterpretationResolution flag) { return flag; } template InterpretationResolution unify(FLAG_A flagA, FLAG_B flagB, FLAGS... flags) { if(flagA==ANY){ return unify(flagB, flags...); } if(flagB==ANY){ return unify(flagA, flags...); } assert(flagA==flagB); return flagA; } template bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); return details::checkConstraints(flag); } template bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); flags.pop_back(); if(details::checkConstraints(flag)){ return checkConstraints(move(flags)); } return false; } bool InterpretationData::isDefault() const { return(resolution==ANY&&op==NONE); } void recognizeTags(const Expression& e) { InterpretationData tag{details::recognizeTags(e.tags), NONE}; if(!tag.isDefault()) Attachments::put(e, tag); } InterpretationResolution recognizeTags(const ManagedFnPtr& f) { return details::recognizeTags(f->getTags()); } InterpretationPass::InterpretationPass(PassManager* manager) : AbstractPass(manager) { - Attachments::init(); + Attachments::init(); Attachments::init(); } void InterpretationPass::run() { ManagedFnPtr f=man->root->begin(); auto& visitedSymbols=getSymbolCache(); while(f.isValid()) { const Symbol&symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; if(!visitedSymbols.isCached(symbolFunction)){ visitedSymbols.setCachedValue(symbolFunction, process(f)); } ++f; } } InterpretationResolution InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl) { recognizeTags(expression); InterpretationResolution resolution=ANY; InterpretationOperator opNo=NONE; switch(expression.__state) { case Expression::NUMBER: case Expression::STRING: { break; } case Expression::IDENT: { resolution=Parent::processSymbol(Attachments::get(expression), context); break; } case Expression::COMPOUND: break; default: { resolution=CMPL_ONLY; break; } } if(expression.__state==Expression::COMPOUND) switch(expression.op) { case Operator::EQU: case Operator::NE: { InterpretationResolution left=process(expression.operands[0], context); InterpretationResolution right=process(expression.operands[1], context); resolution=unify(left, right); break; } case Operator::LOGIC_AND: { assert(expression.operands.size()==1); resolution=process(expression.operands[0], context); break; } case Operator::CALL: { size_t sizeOperands = expression.operands.size(); std::vector operands; operands.reserve(sizeOperands); for(size_t opNo=0; opNo callees=man->root->getFunctionSpecializations(expression.getValueString()); + list callees=man->root->getFnSpecializations(expression.getValueString()); if(callees.size()!=1){ resolution=CMPL_ONLY; break; } ManagedFnPtr callee=callees.front(); const Symbol& symbCalleeFunc{ScopedSymbol::RetSymbol, callee->getEntryScope()}; //recursion-aware processing: // - skip self recursion const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, context.function->getEntryScope()}; if(!(symbSelfFunc==symbCalleeFunc)){ InterpretationResolution resCallee=processFnCall(callee, context); assert(resCallee!=FUNC_POSTPONED&&"Indirect recursion detected: can't decide on interpretation resolution"); resolution=unify(resolution, resCallee); } //check arguments compatibility - const FunctionInterpretationData& calleeSignature=FunctionInterpretationHelper::getSignature(callee); + const I12nFunctionSpec& calleeSignature=FunctionInterpretationHelper::getSignature(callee); for(size_t opNo=0; opNo__identifiers.at(argName), versions::VERSION_NONE}, exprBody }; getSymbolCache().setCachedValue(argS, INTR_ONLY); Parent::process(expression.blocks.front(), context); resolution = CMPL_ONLY; opNo=QUERY_LATE; break; } case Operator::SWITCH_LATE: { resolution = CMPL_ONLY; opNo = SWITCH_LATE; break; } case Operator::IF: { InterpretationResolution flagCondition=process(expression.getOperands()[0], context); InterpretationResolution flagScope1=Parent::process(expression.blocks.front(), context); InterpretationResolution flagScope2=Parent::process(expression.blocks.back(), context); //special case: IF_INTERPRET_CONDITION if(checkConstraints({flagCondition})){ opNo=IF_INTERPRET_CONDITION; flagCondition=ANY; } resolution=unify(flagCondition, flagScope1, flagScope2); break; } case Operator::FOLD: { InterpretationResolution flagInput=process(expression.getOperands()[0], context); InterpretationResolution flagAccumInit=process(expression.getOperands()[1], context); CodeScope* scopeBody=expression.blocks.front(); const std::string& nameEl=expression.bindings[0]; Symbol symbEl{ScopedSymbol {scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody}; getSymbolCache().setCachedValue(symbEl, InterpretationResolution(flagInput)); const std::string& nameAccum=expression.bindings[1]; Symbol symbAccum{ScopedSymbol {scopeBody->__identifiers.at(nameAccum), versions::VERSION_NONE}, scopeBody}; getSymbolCache().setCachedValue(symbAccum, InterpretationResolution(flagAccumInit)); InterpretationResolution flagBody=Parent::process(expression.blocks.front(), context); //special case: FOLD_INTERPRET_INPUT if(checkConstraints({flagInput})){ opNo=FOLD_INTERPRET_INPUT; flagInput=ANY; } resolution=unify(flagInput, flagAccumInit, flagBody); break; } case Operator::INDEX: { for(const Expression &opNo : expression.getOperands()) { resolution=unify(resolution, process(opNo, context)); } break; } case Operator::SWITCH: { InterpretationResolution flagCondition=process(expression.operands[0], context); bool hasDefaultCase=expression.operands[1].op==Operator::CASE_DEFAULT; //determine conditions resolution InterpretationResolution flagHeaders=flagCondition; for(size_t size=expression.operands.size(), i=hasDefaultCase?2:1; i({flagHeaders})){ opNo=SWITCH_INTERPRET_CONDITION; flagHeaders=ANY; } //determine body resolutions resolution=flagHeaders; for(size_t size=expression.operands.size(), i=1; i({resolution})){ opNo=SWITCH_VARIANT; resolution=ANY; } const string identCondition=expression.bindings.front(); for(auto scope : expression.blocks) { //set binding resolution ScopedSymbol symbolInternal=scope->getSymbol(identCondition); getSymbolCache().setCachedValue(Symbol{symbolInternal, scope}, InterpretationResolution(resolutionCondition)); resolution=unify(resolution, Parent::process(scope, context)); } for(auto scope : expression.blocks) { resolution=unify(resolution, Parent::process(scope, context)); } break; } case Operator::LIST: { for(const Expression &opNo : expression.getOperands()) { resolution=unify(resolution, process(opNo, context)); } break; } case Operator::VARIANT: { if(expression.getOperands().size()){ resolution=process(expression.getOperands().front(), context); } else { resolution=ANY; } break; } default: { resolution=CMPL_ONLY; for(const Expression &opNo : expression.getOperands()) { process(opNo, context); } for(CodeScope* scope : expression.blocks) { Parent::process(scope, context); } break; } } InterpretationData dataExpected= Attachments::get(expression,{ANY, NONE}); resolution=unify(resolution, dataExpected.resolution); if(resolution!=dataExpected.resolution || opNo != dataExpected.op ){ Attachments::put(expression,{resolution, opNo}); } return resolution; } InterpretationResolution InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context) { return process(function); } InterpretationResolution InterpretationPass::process(ManagedFnPtr function) { CodeScope* entry=function->getEntryScope(); std::vector arguments=entry->__bindings; const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, function->getEntryScope()}; auto& cache=getSymbolCache(); if(cache.isCached(symbSelfFunc)) return cache.getCachedValue(symbSelfFunc); - const FunctionInterpretationData& fnSignature=FunctionInterpretationHelper::getSignature(function); + const I12nFunctionSpec& fnSignature=FunctionInterpretationHelper::getSignature(function); InterpretationResolution fnResolutionExpected=details::recognizeTags(function->getTags()); //mark preliminary function resolution as expected if(fnResolutionExpected!=ANY){ cache.setCachedValue(symbSelfFunc, move(fnResolutionExpected)); } else { // - in order to recognize indirect recursion mark this function resolution as POSTPONED cache.setCachedValue(symbSelfFunc, FUNC_POSTPONED); } //set resolution for function arguments as expected for(int argNo=0, size=arguments.size(); argNo__identifiers.at(arguments[argNo]), versions::VERSION_NONE}, entry}; cache.setCachedValue(symbArg, InterpretationResolution(fnSignature.signature[argNo])); } PassContext context; context.function=function; context.scope=entry; InterpretationResolution resActual=process(CodeScope::getDefinition(symbSelfFunc), context); resActual=unify(resActual, fnResolutionExpected); return cache.setCachedValue(symbSelfFunc, move(resActual)); } -const FunctionInterpretationData +const I12nFunctionSpec FunctionInterpretationHelper::getSignature(ManagedFnPtr function) { - if(Attachments::exists(function)){ - return Attachments::get(function); + if(Attachments::exists(function)){ + return Attachments::get(function); } - FunctionInterpretationData&& data=recognizeSignature(function); - Attachments::put(function, data); + I12nFunctionSpec&& data=recognizeSignature(function); + Attachments::put(function, data); return data; } -FunctionInterpretationData +I12nFunctionSpec FunctionInterpretationHelper::recognizeSignature(ManagedFnPtr function) { CodeScope* entry=function->__entry; - FunctionInterpretationData result; + I12nFunctionSpec result; result.signature.reserve(entry->__bindings.size()); bool flagPartialInterpretation=false; for(size_t no=0, size=entry->__bindings.size(); no__bindings[no]; Symbol symbArg{ScopedSymbol {entry->__identifiers.at(argName), versions::VERSION_NONE}, entry}; const Expression& arg=CodeScope::getDefinition(symbArg); InterpretationResolution argResolution=details::recognizeTags(arg.tags); flagPartialInterpretation|=(argResolution==INTR_ONLY); result.signature.push_back(argResolution); } result.flagPartialInterpretation=flagPartialInterpretation; return result; } bool FunctionInterpretationHelper::needPartialInterpretation(ManagedFnPtr function) { - const FunctionInterpretationData& data=getSignature(function); + const I12nFunctionSpec& data=getSignature(function); return data.flagPartialInterpretation; } } } //end of namespace xreate::interpretation /** \class xreate::interpretation::InterpretationPass * * The class encapsulates *Interpretation Analysis* to support [Interpretation](/d/concepts/interpretation/). * * It recognizes program functions, expressions, instructions eligible for interpretation - * and stores the output in \ref Attachments and \ref Attachments + * and stores the output in \ref Attachments and \ref Attachments * * There are number of instructions currently eligible for interpretation: * - Basic literals: numbers and strings * - Compounds: lists, structs, variants * - Non-versioned identifiers * - Comparison and logic operators * - %Function calls * - `query` intrinsic function calls * - Branching: `if`, `loop fold`, `switch`, `switch variant` statements * * Some of these instructions are eligible also for *late interpretation* to allow coupling * of compiled instructions with interpreted ones, those are: * - Partial function calls * - Branching: `if`, `loop fold`, `switch`, `switch variant` statements * * \sa xreate::interpretation::TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/) */ diff --git a/cpp/src/pass/interpretationpass.h b/cpp/src/pass/interpretationpass.h index 22702bf..88a76f3 100644 --- a/cpp/src/pass/interpretationpass.h +++ b/cpp/src/pass/interpretationpass.h @@ -1,96 +1,96 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: interpretationpass.h * Author: pgess * * Created on July 5, 2016, 5:21 PM */ #ifndef INTERPRETATIONPASS_H #define INTERPRETATIONPASS_H #include "abstractpass.h" #include #ifndef FRIENDS_INTERPRETATION_TESTS #define FRIENDS_INTERPRETATION_TESTS #endif //TODO refactor interpretation. Get rid of InterpretationOperator, put only one operator - Hybrid/Late. namespace xreate{ namespace interpretation{ enum InterpretationResolution{ANY, INTR_ONLY, CMPL_ONLY, FUNC_POSTPONED}; enum InterpretationOperator{ NONE, IF_INTERPRET_CONDITION, FOLD_INTERPRET_INPUT, QUERY_LATE, SWITCH_INTERPRET_CONDITION, SWITCH_VARIANT, SWITCH_LATE, CALL_INTERPRET_PARTIAL }; struct InterpretationData{ InterpretationResolution resolution; InterpretationOperator op; bool isDefault() const; }; - struct FunctionInterpretationData{ + struct I12nFunctionSpec{ typedef std::vector Signature; Signature signature; bool flagPartialInterpretation; }; class FunctionInterpretationHelper { public: - static const FunctionInterpretationData + static const I12nFunctionSpec getSignature(ManagedFnPtr function); static bool needPartialInterpretation(ManagedFnPtr function); private: - static FunctionInterpretationData recognizeSignature(ManagedFnPtr function); + static I12nFunctionSpec recognizeSignature(ManagedFnPtr function); }; /** \brief Provides the interpretation analysis. Determines parts of a program eligible for Interpretation by \ref xreate::interpretation::TargetInterpretation */ class InterpretationPass: public AbstractPass { typedef AbstractPass Parent; public: InterpretationResolution process(const Expression& expression, PassContext context, const std::string& varDecl="") override; InterpretationResolution process(ManagedFnPtr function); InterpretationResolution processFnCall(ManagedFnPtr function, PassContext context); InterpretationPass(PassManager* manager); void run(); }; namespace details { InterpretationResolution recognizeTags(const std::map& tags); } } //end of namespace interpretation template<> interpretation::InterpretationResolution defaultValue(); template<> -struct AttachmentsDict +struct AttachmentsDict { - typedef interpretation::FunctionInterpretationData Data; + typedef interpretation::I12nFunctionSpec Data; static const unsigned int key = 5; }; template<> struct AttachmentsDict { typedef interpretation::InterpretationData Data; static const unsigned int key = 3; }; } //end of namespace xreate #endif /* INTERPRETATIONPASS_H */ diff --git a/cpp/src/pass/ipass.h b/cpp/src/pass/ipass.h new file mode 100644 index 0000000..442a765 --- /dev/null +++ b/cpp/src/pass/ipass.h @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Author: pgess + * Created on: Jan, 2020 +*/ + +#ifndef XREATE_IPASS_H +#define XREATE_IPASS_H + +namespace xreate{ +class PassManager; + +/** \brief Interface for all passes to inherit. \ref xreate::PassManager holds a collection of passes to execute */ +class IPass{ +public: + IPass(PassManager *manager); + + virtual ~IPass(){} + + /** \brief Executes pass */ + virtual void run() = 0; + + /** \brief Finalizes pass. Empty by default*/ + virtual void finish(); + + PassManager *man; +}; +} + +#endif //XREATE_IPASS_H diff --git a/cpp/src/query/containers.cpp b/cpp/src/query/containers.cpp index 9bfa1a5..2b8fc8c 100644 --- a/cpp/src/query/containers.cpp +++ b/cpp/src/query/containers.cpp @@ -1,111 +1,112 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * containers.cpp * Created on 3/14/15. */ #include #include "query/containers.h" +#include "analysis/resources.h" using namespace std; using namespace xreate::containers; using namespace xreate; Implementation Query::queryImplementation(xreate::Symbol const &s) { if (Attachments::exists(s)) { return Attachments::get(s); } return Implementation::create(s); } Query::Query() { Attachments::init(); } void Query::init(TranscendLayer* transcend) { if (flagDataIsLoaded) return; //Fill implementation data for a data sources: - auto range = transcend->query(Config::get("containers.id.implementations")); + auto range = transcend->query(xreate::analysis::CONTAINERS_ID_IMPL_PREDICATE); if (range.size()) for(auto atom : range) { auto data = TranscendLayer::parse(atom.second); Symbol var = transcend->unpack(get<0>(data)); string implStr = get<1>(data).name().c_str(); - if (implStr == Config::get("containers.impl.solid")) { + if (implStr == xreate::analysis::CONTAINERS_IMPL_SOLID_PREDICATE) { auto size = TranscendLayer::parse(get<1>(data)); Attachments::put(var,{SOLID, ImplementationRec {get<0>(size)}}); - } else if (implStr == Config::get("containers.impl.onthefly")) { + } else if (implStr == xreate::analysis::CONTAINERS_IMPL_ONTHEFLY_PREDICATE) { Attachments::put(var,{ON_THE_FLY, ImplementationRec {var}}); } else { assert(false && "Unable to determine proper implementation for the symbol"); } } flagDataIsLoaded = true; } Implementation Implementation::create(const Symbol &var) { //TODO review implementation determination strategy Expression varDecl = CodeScope::getDefinition(var); switch (varDecl.op) { case Operator::LIST_RANGE: { ImplementationRec rec{var}; return {ON_THE_FLY, rec}; } case Operator::LIST: { return {SOLID, ImplementationRec {varDecl.getOperands().size()}}; } default: break; }; ImplementationLinkedList ill(var); if (ill) { return ill.getImplementationData(); } assert(false && "Unable to determine proper implementation for the symbol"); return Implementation(); } ImplementationLinkedList::ImplementationLinkedList(const Symbol& source) : flagIsValid(false), s(source) { const Expression& sourceExpr = CodeScope::getDefinition(source); - if (sourceExpr.tags.count(Config::get("containers.id.linkedlist"))) { + if (sourceExpr.tags.count(analysis::CONTAINERS_ID_LINKLIST_PREDICATE)) { flagIsValid = true; - Expression tagLinkedlist = sourceExpr.tags.at(Config::get("containers.id.linkedlist")); + Expression tagLinkedlist = sourceExpr.tags.at(analysis::CONTAINERS_ID_LINKLIST_PREDICATE); assert(tagLinkedlist.operands.size() == 2); fieldPointer = tagLinkedlist.operands.at(0).getValueString(); terminator = tagLinkedlist.operands.at(1); } } ImplementationLinkedList::operator bool () const { return flagIsValid; } Implementation ImplementationLinkedList::getImplementationData() const { return {ON_THE_FLY, ImplementationRec{s}}; } diff --git a/cpp/src/query/containers.h b/cpp/src/query/containers.h index 7765645..8f19dd9 100644 --- a/cpp/src/query/containers.h +++ b/cpp/src/query/containers.h @@ -1,96 +1,96 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * * containers.h * Created on 3/14/15. */ /** * \file query/containers.h * \brief Transcend solutions on [Containers](/d/concepts/containers/) implementation details */ #ifndef _XREATE_CONTAINERSQUERY_H_ #define _XREATE_CONTAINERSQUERY_H_ #include "xreatemanager.h" #include "transcendlayer.h" #include namespace xreate { namespace containers { enum ImplementationType {SOLID, ON_THE_FLY, LINKED_LIST}; template struct ImplementationRec; template<> struct ImplementationRec { size_t size; }; template<> struct ImplementationRec{ Symbol source; }; struct Implementation; struct ImplementationLinkedList { bool flagIsValid; std::string fieldPointer; Expression terminator; ImplementationLinkedList(const Symbol& source); operator bool() const; Implementation getImplementationData() const; private: Symbol s; }; struct Implementation { typedef boost::variant, ImplementationRec> Variant; ImplementationType impl; Variant data; static Implementation create(const Symbol &var); static Implementation create(const Symbol& var, const std::string &implSerialized); template const ImplementationRec& extract() const{ const ImplementationRec& rec = boost::get>(data); return rec; } }; /** * \brief Queries Transcend solutions on containers implementation details - * \sa xreate::containers::Iterator + * \sa xreate::containers::IFwdIteratorIR */ class Query : public xreate::IQuery { public: static Implementation queryImplementation(xreate::Symbol const &s); void init(TranscendLayer* transcend); Query(); ~Query(){} private: bool flagDataIsLoaded = false; PassManager *man; }; } template<> struct AttachmentsDict { typedef containers::Implementation Data; static const unsigned int key = 1; }; } #endif //_XREATE_CONTAINERSQUERY_H_ diff --git a/cpp/src/query/demand.cpp b/cpp/src/query/demand.cpp new file mode 100644 index 0000000..a2c791d --- /dev/null +++ b/cpp/src/query/demand.cpp @@ -0,0 +1,45 @@ +// +// Created by pgess on 25/01/2020. +// + +#include "query/demand.h" +#include "analysis/resources.h" +using namespace xreate::demand; +using namespace std; +void +DemandQuery::init(TranscendLayer* transcend){ + const StaticModel& modelF = transcend->query(analysis::DEMAND_FORMAL_PREDICATE); + for (const auto& entry: modelF){ + std::string fn, arg, typeAlias; + tie(fn, arg, typeAlias) = TranscendLayer::parse(schemeT{}, entry.second); + __demand[fn].emplace(arg, typeAlias); + } + + const StaticModel& modelA = transcend->query(analysis::DEMAND_ACTUAL_PREDICATE); + for (const auto& entry: modelA){ + ASTSitePacked site; + std::string arg; + Gringo::Symbol value; + tie(site, arg, value) = TranscendLayer::parse(schemeT{}, entry.second); + + __supply[transcend->unpack(site)].emplace(arg, value); + } +} + +Demand +DemandQuery::getFnDemand(const std::string& fn){ + if(__demand.count(fn)) + return __demand.at(fn); + + return Demand(); +} + +Supply +DemandQuery::getFnSupply(const ASTSite& siteCall){ + if(__supply.count(siteCall)) + return __supply.at(siteCall); + + return Supply(); +} + + diff --git a/cpp/src/query/demand.h b/cpp/src/query/demand.h new file mode 100644 index 0000000..959bf2b --- /dev/null +++ b/cpp/src/query/demand.h @@ -0,0 +1,30 @@ +// +// Created by pgess on 19/01/2020. +// + +#ifndef XREATE_DEMAND_H +#define XREATE_DEMAND_H + +#include "transcendlayer.h" + +namespace xreate{ namespace demand{ + +typedef std::map Demand; //key->type +typedef std::map Supply; + +class DemandQuery: public IQuery{ +public: + Demand getFnDemand(const std::string& fn); + Supply getFnSupply(const ASTSite& siteCall); + +public: + void init(TranscendLayer* transcend) override; + +private: + std::map __demand; + std::map __supply; +}; + +}} //end of namespace xreate::demand + +#endif //XREATE_DEMAND_H diff --git a/cpp/src/query/latex.cpp b/cpp/src/query/latex.cpp index 6970cc7..c20dac8 100644 --- a/cpp/src/query/latex.cpp +++ b/cpp/src/query/latex.cpp @@ -1,101 +1,101 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Author: pgess * Created on June 25, 2018, 12:14 PM * * \file latex.cpp * \brief latex */ #include "query/latex.h" #include "aux/transcend-decorators.h" using namespace std; using namespace xreate::latereasoning; namespace xreate{ namespace latex{ void LatexQuery::init(TranscendLayer* transcend) { __transcend = transcend; //schema: latex_fn_demand(Fn, Subject) StaticModel data = __transcend->query("latex_fn_demand_ordered"); for(const auto& entry : data) { string fnName, subject; size_t id; tie(fnName, subject, id) = __transcend->parse(entry.second); __demand[fnName].resize(std::max(__demand[fnName].size(), id+1)); __demand[fnName][id] = subject; } //schema: latex_registered_subjects(Subject, Decision) data = __transcend->query("latex_registered_subjects"); for(const auto& entry : data) { string subject; Gringo::Symbol decision; tie(subject, decision) = __transcend->parse(entry.second); __domains[subject].push_back(decision); } //schema: latex_decision(Scope, Subject, Decision) data = __transcend->query("latex_decision"); for(const auto& entry : data) { ScopePacked scope; string subject; Gringo::Symbol decision; tie(scope, subject, decision) = __transcend->parse(entry.second); - __decisions[make_pair(scope, subject)] = entry.second; + __valuesStatic[make_pair(scope, subject)] = entry.second; } //schema: latex_parameters_offset(int) //Override parameter from transcend data = __transcend->query("latex_parameters_offset"); if(data.size()) { LatexParametersOffset = std::get<0>(__transcend->parse(data.begin()->second)); } auto transcendLate = Decorators::getInterface(__transcend); //Later decisions. schema: Scope, Subject, Decision LateAnnotationsGroup group = transcendLate->queryLate("latex_decision"); for (auto entry: group.annotations) { auto key = __transcend->parse(entry.first); - __decisionsLate.emplace(make_pair(get<0>(key), get<1>(key)), entry.second); + __valuesLate.emplace(make_pair(get<0>(key), get<1>(key)), entry.second); } } Demand LatexQuery::getFnDemand(const std::string& fnName) { if (!__demand.count(fnName)) return Demand(); return __demand.at(fnName); } latereasoning::LateAnnotation -LatexQuery::getDecision(const std::string& subject, const CodeScope* scopeCaller) { - ScopePacked scopeP = __transcend->pack(scopeCaller); +LatexQuery::getValue(const std::string& key, const CodeScope* context) { + ScopePacked scopeP = __transcend->pack(context); - if(__decisions.count(make_pair(scopeP, subject))){ + if(__valuesStatic.count(make_pair(scopeP, key))){ //found static decision - return LateAnnotation(__decisions.at(make_pair(scopeP, subject))); + return LateAnnotation(__valuesStatic.at(make_pair(scopeP, key))); } - return __decisionsLate.at(make_pair(scopeP, subject)); + return __valuesLate.at(make_pair(scopeP, key)); } std::list -LatexQuery::getSubjectDomain(const std::string& subject) { - assert(__domains.count(subject)); - return __domains.at(subject); +LatexQuery::getDomain(const std::string& key) { + assert(__domains.count(key)); + return __domains.at(key); } } } diff --git a/cpp/src/query/latex.h b/cpp/src/query/latex.h index c66f39f..e842936 100644 --- a/cpp/src/query/latex.h +++ b/cpp/src/query/latex.h @@ -1,45 +1,45 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /** * \file query/latex.h * \brief Transcend solution on [Context](/d/concepts/context/) implementation details */ #ifndef LATEXQUERY_H #define LATEXQUERY_H #include "transcendlayer.h" #include "aux/latereasoning.h" #include namespace xreate{ namespace latex{ typedef std::vector Demand; /** \brief Queries Transcend solution on [Context](/d/concepts/context/) implementation details */ class LatexQuery: public IQuery{ public: VNameId LatexParametersOffset = 1000; //Default value. Overriden by `latex_parameters_offset` from transcend Demand getFnDemand(const std::string& fnName); - latereasoning::LateAnnotation getDecision(const std::string& subject, const CodeScope* scopeCaller); - std::list getSubjectDomain(const std::string& subject); + latereasoning::LateAnnotation getValue(const std::string& key, const CodeScope* context); + std::list getDomain(const std::string& key); void init(TranscendLayer* transcend); private: TranscendLayer* __transcend; std::map __demand; - std::map, Gringo::Symbol> __decisions; - std::map, latereasoning::LateAnnotation> __decisionsLate; + std::map, Gringo::Symbol> __valuesStatic; + std::map, latereasoning::LateAnnotation> __valuesLate; std::map> __domains; }; } } #endif diff --git a/cpp/src/query/polymorph.cpp b/cpp/src/query/polymorph.cpp index 52f6aa0..7a4b3fd 100644 --- a/cpp/src/query/polymorph.cpp +++ b/cpp/src/query/polymorph.cpp @@ -1,71 +1,43 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: polymorph.cpp * Author: pgess * * Created on November 9, 2017, 12:14 PM */ #include "polymorph.h" #include "aux/transcend-decorators.h" +#include "analysis/resources.h" + using namespace std; +using namespace xreate::analysis; namespace xreate{ namespace polymorph{ -const std::string atomPolymorph = "dfa_callguard"; - void -PolymorphQuery::init(TranscendLayer* transcend) { - __transcend = transcend; - - StaticModel queryResult = transcend->query(atomPolymorph); - if (queryResult.size()) { - for (auto entry : queryResult) { - auto answer = TranscendLayer::parse(entry.second); - SymbolNode symbCaller = std::get<0>(answer); - SymbolGeneralized symbCallerUnpacked = transcend->unpack(symbCaller); - - __cacheEarlyReasoning.emplace(symbCallerUnpacked, entry.second); - } - } - - auto transcendLate = - Decorators::getInterface(__transcend); - latereasoning::LateAnnotationsGroup group = transcendLate->queryLate(atomPolymorph); - - for(auto entry : group.annotations) { - auto targetWrapper = Gringo::Symbol::createTuple(Gringo::SymSpan{&entry.first, 1}); - auto targetSP = __transcend->parse(targetWrapper); - SymbolGeneralized targetS = __transcend->unpack(std::get<0>(targetSP)); - __cacheLateReasoning.emplace(targetS, entry.second); - } -} - -latereasoning::LateAnnotation -PolymorphQuery::get(const Expression& e) const { - SymbolGeneralized targetS = Attachments::exists(e) ? - SymbolGeneralized(Attachments::get(e)) - : SymbolGeneralized(SymbolAnonymous{e.id}); - - if (__cacheEarlyReasoning.count(targetS)) { - return latereasoning::LateAnnotation(__cacheEarlyReasoning.at(targetS)); - } - - if (__cacheLateReasoning.count(targetS)) { - return __cacheLateReasoning.at(targetS); +PolymorphQuery::init(TranscendLayer *transcend){ + StaticModel model = transcend->query(POLYMORPH_SUPPLY_PREDICATE); + if(model.size()){ + for(const auto& entry : model){ + ASTSitePacked sitePacked; Gringo::Symbol value; string schemeStr; + tie(sitePacked, value, schemeStr) = TranscendLayer::parse(schemeT{}, entry.second); + TypeAnnotation schemeRaw(TypeOperator::ALIAS, {}); + schemeRaw.__valueCustom = schemeStr; + ExpandedType scheme = transcend->ast->expandType(schemeRaw); + __modelEarlyReasoning.emplace( + transcend->unpack(sitePacked), + make_pair(value, scheme) + ); } - - assert(false && "Can't find a guard"); -} - -Expression -PolymorphQuery::getValue(const Gringo::Symbol& symbol) const { - auto result = __transcend->parse(symbol); - return std::get<1>(result); + } } +std::pair +PolymorphQuery::getFnSupply(const ASTSite& siteCall){ + return __modelEarlyReasoning.at(siteCall); } -} //end of xreate::polymorph +}} //end of xreate::polymorph diff --git a/cpp/src/query/polymorph.h b/cpp/src/query/polymorph.h index 7be4f56..79d8c46 100644 --- a/cpp/src/query/polymorph.h +++ b/cpp/src/query/polymorph.h @@ -1,42 +1,41 @@ /* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * File: polymorph.h * Author: pgess * * Created on November 9, 2017, 12:14 PM */ /** * \file query/polymorph.h * \brief Transcend solution on [Polymorphism](/d/concepts/polymorphism/) implementation details */ #ifndef POLYMORPHQUERY_H #define POLYMORPHQUERY_H #include "transcendlayer.h" #include "aux/latereasoning.h" #include namespace xreate { namespace polymorph { /** \brief Queries Transcend solution on [Polymorphism](/d/concepts/polymorphism/) implementation details */ class PolymorphQuery: public IQuery { public: - latereasoning::LateAnnotation get(const Expression& e) const; - Expression getValue(const Gringo::Symbol& s) const; - virtual void init(TranscendLayer* transcend) override; - + std::pair getFnSupply(const ASTSite& siteCall); + +public: + virtual void init(TranscendLayer* transcend) override; + private: - std::unordered_map __cacheEarlyReasoning; - std::unordered_map __cacheLateReasoning; - TranscendLayer* __transcend = nullptr; + std::map> __modelEarlyReasoning; }; }}//end of xreate::polymorph #endif /* POLYMORPHQUERY_H */ diff --git a/cpp/src/transcendlayer.cpp b/cpp/src/transcendlayer.cpp index 2ecd979..e158adc 100644 --- a/cpp/src/transcendlayer.cpp +++ b/cpp/src/transcendlayer.cpp @@ -1,495 +1,404 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: transcendlayer.cpp */ /** * \file transcendlayer.h * \brief Transcend reasoning implementation */ #include "transcendlayer.h" +#include "analysis/resources.h" #include "analysis/utils.h" #include "utils.h" #include #include #include #include #include #include using namespace std; +using namespace xreate::analysis; -//TODO escape identifiers started with upper case symbol +//namespace xreate{ -namespace xreate{ - -bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2) { - return s1.id == s2.id && s1.flagIsUsed == s2.flagIsUsed; -} - -struct VisitorSymbolNodeHash : public boost::static_visitor{ - - std::size_t operator()(const xreate::SymbolPacked& node) const noexcept { - return 2 * (node.identifier + 3 * node.scope + 5 * std::abs(node.version)); - } - - std::size_t operator()(const xreate::SymbolAnonymous& node) const noexcept { - return 7 * node.id; - } -} ; -} - -namespace std{ - -std::size_t -hash::operator()(xreate::SymbolNode const& s) const noexcept { - return boost::apply_visitor(xreate::VisitorSymbolNodeHash(), s); -} - -std::size_t -hash::operator()(xreate::SymbolGeneralized const& s) const noexcept { - return xreate::AttachmentsId::getId(s); -} -} +//bool operator==(const Site& s1, const Site& s2) { +// return s1.id == s2.id && s1.flagIsUsed == s2.flagIsUsed; +//} namespace xreate{ void TranscendLayer::printWarnings(std::ostream& out) { const std::string warningTag = "warning"; auto warningsModel = query(warningTag); if(warningsModel.size()) for(auto warning : warningsModel) { unsigned int warningId; Gringo::Symbol params; std::tie(warningId, params) = parse(warning.second); cout << "Warning: " << __warnings.at(warningId) << " "; params.print(out); out << params; } } bool TranscendLayer::processSolution(Gringo::Model const &model) { cout << "Model: " << endl; - const string& atomBindVar = Config::get("transcend.bindings.variable"); - const string& atomBindFunc = Config::get("transcend.bindings.function"); - const string& atomBindScope = Config::get("transcend.bindings.scope"); + const string& atomBindVar = VAR_ANN_PREDICATE; + const string& atomBindFunc = FUNCTION_ANN_PREDICATE; + const string& atomBindScope = SCOPE_ANN_PREDICATE; for(Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) { atom.print(cout); cout << " | " << endl; string atomName(atom.name().c_str()); if(atomName == atomBindVar || atomName == atomBindFunc || atomName == atomBindScope) { string atomAlias = std::get<1>(parse(atom)).name().c_str(); __model.emplace(atomAlias, atom); continue; } __model.emplace(atomName, atom); } return true; } void TranscendLayer::registerReport(IAnalysisReport * report) { __reports.push_back(report); } void TranscendLayer::printReports() { for(IAnalysisReport* report : __reports) { report->print(__partGeneral); } } void TranscendLayer::deleteReports(){ for(IAnalysisReport* report : __reports) { delete report; } __reports.clear(); } void TranscendLayer::addRuleWarning(const RuleWarning & rule) { - //__partGeneral << rule << endl; - - list domains; - boost::format formatDef("%1%(%2%)"); - std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(domains, domains.begin()), - [&formatDef](const std::pair &argument) { - string domain; - switch(argument.second) { - case DomainAnnotation::FUNCTION: - domain = "function"; - break; - case DomainAnnotation::VARIABLE: - domain = "variable"; - break; - } - - return boost::str(formatDef % domain % argument.first); - }); - - list vars; - std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(vars, vars.begin()), - [](const std::pair &argument) { - return argument.first.c_str(); - }); - - list> guardsRaw; - std::transform(rule.__guards.begin(), rule.__guards.end(), std::inserter(guardsRaw, guardsRaw.begin()), - [this](const Expression & guard) { - return xreate::analysis::compile(guard); - }); - - const list& guards = xreate::analysis::multiplyLists(std::move(guardsRaw)); - list &&branches = xreate::analysis::compileNeg(rule.__condition); - - boost::format formatWarning("warning(%1%, (%2%)):- %3%, %4%, %5%."); - for(const string &guardsJoined : guards) - for(const string &branch : branches) { - unsigned int hook = registerWarning(string(rule.__message)); - - __partGeneral << formatWarning - % (hook) - % (boost::algorithm::join(vars, ", ")) - % (branch) - % (guardsJoined) - % (boost::algorithm::join(domains, ", ")) - << endl; - } + //FIX } unsigned int TranscendLayer::registerWarning(std::string && message) { static int warningId = 0; __warnings.emplace(warningId, message); return warningId++; } void -TranscendLayer::involveImports() { +TranscendLayer::appendImport() { ostream &out = __partGeneral; if(ast) for(string fn : ast->__rawImports) { std::ifstream file(fn); if(!file) { std::cout << "Can't process script file: " << fn << std::endl; assert(false); } while(!file.eof()) { string line; std::getline(file, line); out << line << endl; } } } void TranscendLayer::addRawScript(std::string && script) { __partGeneral << script; } void TranscendLayer::run() { - involveImports(); + appendImport(); printReports(); ostringstream program; program << __partTags.str() << __partGeneral.str(); cout << FYEL(program.str()) << endl; std::vector args{"clingo", nullptr}; DefaultGringoModule moduleDefault; Gringo::Scripts scriptsDefault(moduleDefault); ClingoLib ctl(scriptsDefault, 0, args.data(), { }, 0); ctl.add("base",{}, program.str()); ctl.ground({ {"base", {}} }, nullptr); // solve Gringo::SolveResult result = ctl.solve([this](Gringo::Model const &model) { this->processSolution(model); return true; }, { }); if(result.satisfiable() == Gringo::SolveResult::Satisfiable) { cout << FGRN("SUCCESSFULLY") << endl; } else { cout << FRED("UNSUCCESSFULLY") << endl; } // invoke all query plugins to process solution for(auto q : __queries) { q.second->init(this); } } TranscendLayer::TranscendLayer() : ast(nullptr) { } StaticModel TranscendLayer::query(const std::string & atom) const { StaticModel result; if (! __model.count(atom)) { return result; } auto currentDataRange = __model.equal_range(atom); std::copy(currentDataRange.first, currentDataRange.second, std::inserter(result, result.end())); return result; } ScopePacked TranscendLayer::pack(const CodeScope * const scope) { auto pos = __indexScopes.emplace(scope, __indexScopes.size()); if(pos.second) __registryScopes.push_back(scope); return pos.first->second; } size_t TranscendLayer::getScopesCount() const { return __registryScopes.size(); } SymbolPacked TranscendLayer::pack(const Symbol& symbol, std::string hintSymbolName) { SymbolPacked result(symbol.identifier.id, symbol.identifier.version, pack(symbol.scope)); __indexSymbolNameHints.emplace(result, hintSymbolName); return result; } Symbol TranscendLayer::unpack(const SymbolPacked & symbol) const { return Symbol{ScopedSymbol {symbol.identifier, symbol.version}, __registryScopes[symbol.scope]}; }; +ASTSitePacked +TranscendLayer::pack(const ASTSite& s){ + ASTSitePacked result; + + if(Attachments::exists(s.id)){ + Symbol resultS = Attachments::get(s.id); + result = ASTSitePacked(pack(resultS)); + + } else{ + result = ASTSitePacked(s); + } + + return result; +} + +ASTSite +TranscendLayer::unpack(const ASTSitePacked& s){ + switch(s.tag){ + case ASTSitePacked::SYMBOL: + return {CodeScope::getDefinition(unpack(s.symbol), true).id}; + + case ASTSitePacked::ANON: + return s.site; + } + assert(false); + return ASTSite(); +} + std::string TranscendLayer::getHintForPackedSymbol(const SymbolPacked & symbol) { auto result = __indexSymbolNameHints.find(symbol); return(result == __indexSymbolNameHints.end()) ? "" : result->second; } IQuery * TranscendLayer::registerQuery(IQuery *query, const QueryId & id) { return __queries.emplace(id, query).first->second; } IQuery * TranscendLayer::getQuery(const QueryId & id) { assert(__queries.count(id) && "Undefined query"); return __queries.at(id); } -class VisitorUnpackSymbol : public boost::static_visitor{ -public: - - VisitorUnpackSymbol(const TranscendLayer* transcend) : __transcend(transcend) { } - - SymbolGeneralized operator()(const SymbolPacked& symbol) const { - return __transcend->unpack(symbol); - } - - SymbolGeneralized operator()(const SymbolAnonymous& symbol) const { - return symbol; - } - -private: - const TranscendLayer* __transcend; -} ; - -class VisitorPackSymbol : public boost::static_visitor{ -public: - - VisitorPackSymbol(TranscendLayer* transcend, const std::string& hintSymbolName) - : __transcend(transcend), __hint(hintSymbolName) { } - - SymbolNode operator()(const Symbol& symbol) const { - return __transcend->pack(symbol, __hint); - } - - SymbolNode operator()(const SymbolAnonymous& symbol) const { - return symbol; - } - -private: - TranscendLayer* __transcend; - std::string __hint; -} ; - -SymbolNode -TranscendLayer::pack(const SymbolGeneralized& symbol, const std::string & hintSymbolName) { - return boost::apply_visitor(VisitorPackSymbol(this, hintSymbolName), symbol); -} - -SymbolGeneralized -TranscendLayer::unpack(const SymbolNode & symbol) const { - return boost::apply_visitor(VisitorUnpackSymbol(this), symbol); -} - bool operator==(const SymbolPacked& s1, const SymbolPacked & s2) { return s1.identifier == s2.identifier && s1.scope == s2.scope; } bool operator<(const SymbolPacked& s1, const SymbolPacked & s2) { return s1.scope < s2.scope || (s1.scope == s2.scope && s1.identifier < s2.identifier); } Expression ParseImplAtom ::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Num: return Expression(atom.num()); case Gringo::SymbolType::Str: return Expression(Atom(std::string(atom.string().c_str()))); case Gringo::SymbolType::Fun: { //FUNC Expression result(Operator::CALL,{Expression(Atom(std::string(atom.name().c_str())))}); for(const Gringo::Symbol& arg : atom.args()) { result.addArg(ParseImplAtom::get(arg)); } return result; } default: { assert(false); } } } int -ParseImplAtom -::get(const Gringo::Symbol & atom) { +ParseImplAtom::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Num: return atom.num(); default: break; } assert(false && "Inappropriate symbol type"); } std::string -ParseImplAtom -::get(const Gringo::Symbol & atom) { +ParseImplAtom::get(const Gringo::Symbol & atom) { switch(atom.type()) { case Gringo::SymbolType::Str: return atom.string().c_str(); case Gringo::SymbolType::Fun: return atom.name().c_str(); default: break; } assert(false && "Inappropriate symbol type"); } SymbolPacked -ParseImplAtom -::get(const Gringo::Symbol & atom) { +ParseImplAtom::get(const Gringo::Symbol & atom) { auto result = TranscendLayer::parse(atom); return SymbolPacked(std::get<0>(result), std::get<1>(result), std::get<2>(result)); }; Gringo::Symbol -ParseImplAtom -::get(const Gringo::Symbol & atom) { +ParseImplAtom::get(const Gringo::Symbol & atom) { return atom; } -SymbolNode -ParseImplAtom -::get(const Gringo::Symbol & atom) { - assert(atom.type() == Gringo::SymbolType::Fun - && "Inappropriate symbol type"); +ASTSitePacked +ParseImplAtom::get(const Gringo::Symbol & atom) { + assert(atom.type() == Gringo::SymbolType::Fun + && "Inappropriate symbol type"); - if(atom.name() == "a") { - return SymbolAnonymous{(unsigned int) std::get<0>(TranscendLayer::parse(atom))}; + if(atom.name() == SITE_ANON_PREDICATE) { + return ASTSitePacked(ASTSite{std::get<0>(TranscendLayer::parse(atom))}); - } else if(atom.name() == "s") { - return ParseImplAtom::get(atom); - } + } else if(atom.name() == SITE_SYMBOL_PREDICATE) { + return ASTSitePacked(ParseImplAtom::get(atom)); + } - assert(false && "Wrong symbol format"); + assert(false && "Wrong symbol format"); } -class VisitorSymbolId : public boost::static_visitor{ -public: +std::string +ASTSitePacked::str() const { + boost::format formatSymbAnonymous(string(SITE_ANON_PREDICATE) + "(%1%)"); + boost::format formatSymbNamed(string(SITE_SYMBOL_PREDICATE) + "(%1%,%2%,%3%)"); - unsigned int operator()(const Symbol& symbol) const { - return AttachmentsId::getId(symbol); + switch(tag){ + case ASTSitePacked::SYMBOL:{ + return boost::str(formatSymbNamed % symbol.identifier % symbol.version % symbol.scope); } - unsigned int operator()(const SymbolAnonymous& symbol) const { - return symbol.id; + case ASTSitePacked::ANON:{ + return boost::str(formatSymbAnonymous % site.id); } -} ; - -unsigned int -AttachmentsId -::getId(const SymbolGeneralized & symbol) { - return boost::apply_visitor(VisitorSymbolId(), symbol); + } } +//unsigned int +//AttachmentsId::getId(const ASTSite& symbol) { +// return boost::apply_visitor(VisitorSymbolId(), symbol); +//} + } //end of xreate namespace /** * \class xreate::TranscendLayer * \brief Logic reasoning implementation. Internally, it's a proxy to the external ASP solver [Clasp](https://potassco.org/clasp/) * * Performs reasoning over source codes in order to facilitate efficient compilation using results from a number of internal analyzers. * Clients implement \ref IAnalysisReport to supply Transcend with data and implement \ref IQuery to find out resolutions. * * Transcend uses the following sources to build a logic program before actual reasoning is performed: * - Raw content. Clients are free to include arbitrary ASP format data in the logic program. See \ref addRawScript(). - * - External scripts. External files with ASP scripts can be appended to the logic program. See `involveImports()` (private member). + * - External scripts. External files with ASP scripts can be appended to the logic program. See `appendImport()` (private member). * - Diagnostic rules to produce diagnostic messages during * compilation(warnings) or even to signal to halt compilation with errors. See \ref addRuleWarning(), \ref registerWarning(). * - Internal analyzers. The analyzer can publish logic facts and rules by implementing \ref IAnalysisReport interface. * * Generally, Transcend input could be loosely broke down into three categories: * - Internally derived data. CFA, DFA, and other analyzers automatically supply the reasoner with * useful insights about source codes, the structure and algorithms of a program. * - User provided custom data. Analyzers extract manual developer-provided annotations from the source codes. * - External data. External files supply reasoner with third-party data * which relates to different aspects of a program possibly produced by external analyzers. * * After Transcend has gathered data from all providers and the logic program is fully constructed, * it runs the external logic reasoner to receive back the solution. * * The solution from the external logic reasoner is accessible via *queries*. * Classes which want to request data from Transcend should implement the \ref IQuery interface. See \ref IQuery descendants to find out currently available queries. * * \section tl_adapt Adaptability * Decorators are used to extend %TranscendLayer functionality. The default bundle defined by \ref DefaultTranscendLayerImpl. * * \sa See xreate::dfa::DFAPass, xreate::cfa::CFAPass, xreate::IQuery, xreate::IAnalysisReport */ diff --git a/cpp/src/transcendlayer.h b/cpp/src/transcendlayer.h index 6ad56de..59634cc 100644 --- a/cpp/src/transcendlayer.h +++ b/cpp/src/transcendlayer.h @@ -1,284 +1,311 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Author: pgess * File: transcendlayer.h */ #ifndef transcendLAYER_H #define transcendLAYER_H #include "ast.h" #include "contextrule.h" #include #include #include #include #include #include #include #include #include #include namespace xreate { typedef unsigned int ScopePacked; const ScopePacked SCOPE_ABSTRACT_GLOBAL = std::numeric_limits::max(); struct SymbolPacked { SymbolPacked(){} SymbolPacked(ScopedSymbol i, ScopePacked s): identifier(i.id), version(i.version), scope(s){} SymbolPacked(VNameId symbolId, versions::VariableVersion symbolVersion, ScopePacked symbolScope) : identifier(symbolId), version(symbolVersion), scope(symbolScope){} VNameId identifier; versions::VariableVersion version; ScopePacked scope; }; bool operator==(const SymbolPacked& s1, const SymbolPacked& s2); bool operator<(const SymbolPacked& s1, const SymbolPacked& s2); -struct SymbolAnonymous { - unsigned int id; - bool flagIsUsed = false; +struct ASTSitePacked { + union { + ASTSite site; + SymbolPacked symbol; + }; + enum { SYMBOL, ANON } tag; + + ASTSitePacked(){} + explicit ASTSitePacked(const ASTSite& data): site(data), tag(ASTSitePacked::ANON) {} + explicit ASTSitePacked(const SymbolPacked& data): symbol(data), tag(ASTSitePacked::SYMBOL){} + std::string str() const; }; -bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2); +//bool operator==(const Site& s1, const Site& s2); -typedef boost::variant SymbolNode; - -//DEBT use SymbolGeneralized communicating with Analysis rather than Symbol -typedef boost::variant SymbolGeneralized; - -template<> -struct AttachmentsId{ - static unsigned int getId(const SymbolGeneralized& symbol); -}; +//template<> +//struct AttachmentsId{ +// static unsigned int getId(const Site& symbol); +//}; } namespace std { - template<> - struct hash { - std::size_t operator()(xreate::SymbolNode const& s) const noexcept; - }; - - template<> - struct hash { - std::size_t operator()(xreate::SymbolGeneralized const& s) const noexcept; - }; +// template<> +// struct hash { +// std::size_t operator()(xreate::SymbolNode const& s) const noexcept; +// }; +// +// template<> +// struct hash { +// std::size_t operator()(xreate::Site const& s) const noexcept; +// }; } namespace xreate { enum class DFGConnection { STRONG, WEAK, PROTOTYPE }; /** \brief Supplies \ref TranscendLayer with results of an analysis*/ class IAnalysisReport { public: /** \brief Composes a logic program to represent the analysis data in ASP format, and appends to a stream*/ virtual void print(std::ostringstream& output) const = 0; virtual ~IAnalysisReport(){}; }; /** \brief Transcend solutions querying interface */ class IQuery { public: virtual void init(TranscendLayer* transcend) = 0; virtual ~IQuery() {} }; enum class QueryId { ContainersQuery, PolymorphQuery, - LatexQuery + LatexQuery, + DemandQuery }; namespace dfa{ class DFAGraph; } namespace cfa { class CFAGraph; } +template struct schemeT{}; typedef std::multimap StaticModel; typedef StaticModel::const_iterator StaticModelIterator; class TranscendLayer { friend class ContextRule; /**\name Data Providers Management */ ///@{ public: void registerReport(IAnalysisReport* report); void printReports(); void deleteReports(); /** \brief Appends arbitrary string to a logic program */ void addRawScript(std::string&& script); private: std::list __reports; /** Includes external text files to a *logic program* */ - void involveImports(); + void appendImport(); ///@} /**\name Queries Management */ ///@{ public: /** \brief Registers a query. See xreate::IQuery */ IQuery* registerQuery(IQuery* query, const QueryId& id); /** \brief Returns a particular query. See xreate::IQuery */ IQuery* getQuery(const QueryId& id); template static std::tuple parse(const Gringo::Symbol& atom); + + template + static std::tuple parse(schemeT>, const Gringo::Symbol& atom); + template + static Ret parseAtom(const Gringo::Symbol& atom); + StaticModel query(const std::string& atom) const; size_t getScopesCount() const; SymbolPacked pack(const Symbol& symbol, std::string hintSymbolName = ""); ScopePacked pack(const CodeScope * const scope); Symbol unpack(const SymbolPacked& symbol) const; - SymbolNode pack(const SymbolGeneralized& symbol, const std::string& hintSymbolName); - SymbolGeneralized unpack(const SymbolNode& symbol) const; +// SymbolNode pack(const Site& symbol, const std::string& hintSymbolName); +// Site unpack(const SymbolNode& symbol) const; std::string getHintForPackedSymbol(const SymbolPacked& symbol); + ASTSitePacked pack(const ASTSite& s); + ASTSite unpack(const ASTSitePacked& s); ///@} private: std::map __queries; std::map __indexSymbolNameHints; std::unordered_map __indexScopes; std::vector __registryScopes; /**\name Diagnostic */ ///@{ //TODO diagnostic move over to separate provider/query public: /** \brief Registers diagnostic rules */ void addRuleWarning(const RuleWarning &rule); /** \brief Registers diagnostic messages */ unsigned int registerWarning(std::string &&message); private: std::map __warnings; void printWarnings(std::ostream& out); ///@} ///@{ public: TranscendLayer(); /** \brief Executes reasoning */ void run(); ///@} AST *ast; protected: virtual bool processSolution(Gringo::Model const &model); private: StaticModel __model; std::ostringstream __partTags; std::ostringstream __partGeneral; }; template struct ParseImplAtom { static typ get(const Gringo::Symbol& atom) { return atom.num(); } }; template<> struct ParseImplAtom { static int get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static std::string get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static SymbolPacked get(const Gringo::Symbol& atom); }; template<> -struct ParseImplAtom { - static SymbolNode get(const Gringo::Symbol& atom); +struct ParseImplAtom { + static Gringo::Symbol get(const Gringo::Symbol& atom); }; template<> -struct ParseImplAtom { - static Gringo::Symbol get(const Gringo::Symbol& atom); +struct ParseImplAtom { + static ASTSitePacked get(const Gringo::Symbol& atom); }; template struct ParseImplAtom>{ static std::list get(const Gringo::Symbol& atom){ bool flagIsList = (atom.type() == Gringo::SymbolType::Fun) && atom.name().empty(); std::list result; if(!flagIsList) { //treat as degenerate case: list with a single element result.push_back(ParseImplAtom::get(atom)); return result; } for (const Gringo::Symbol& arg: atom.args()) { result.push_back(ParseImplAtom::get(arg)); } return result; } }; template<> struct ParseImplAtom { static Expression get(const Gringo::Symbol& atom); }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { const size_t tupleSize = std::tuple_size::value; typedef typename std::tuple_element < tupleSize - index, Tuple>::type ElType; ElType& el = std::get < tupleSize - index > (tup); Gringo::Symbol atom = *arg; el = ParseImplAtom::get(atom); Parse_Impl ::parse(tup, ++arg); } }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { } }; template std::tuple TranscendLayer::parse(const Gringo::Symbol& atom) { typedef std::tuple < Types...> Tuple; Tuple tup; Parse_Impl::value>::parse(tup, atom.args().first); return tup; } + +template +std::tuple +TranscendLayer::parse(schemeT>, const Gringo::Symbol& atom){ + return parse(atom); +} + +template +Ret +TranscendLayer::parseAtom(const Gringo::Symbol& atom){ + return ParseImplAtom::get(atom); +} + + } //end of xreate namespace #endif diff --git a/cpp/src/utils.cpp b/cpp/src/utils.cpp index a901e38..7f55338 100644 --- a/cpp/src/utils.cpp +++ b/cpp/src/utils.cpp @@ -1,38 +1,32 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * utils.cpp * * Author: pgess */ /** * \file cpp/src/utils.h * \brief Auxiliary code */ #include "utils.h" #include using namespace xreate; -Config Config::__self = Config(); - -Config::Config() -: __storage{json_file{ "config/default.json" }} -{} - using boost::locale::conv::utf_to_utf; std::wstring utf8_to_wstring(const std::string& str) { - return utf_to_utf(str.c_str(), str.c_str() + str.size()); + return utf_to_utf(str.c_str(), str.c_str() + str.size()); } std::string wstring_to_utf8(const std::wstring& str) { return utf_to_utf(str.c_str(), str.c_str() + str.size()); } diff --git a/cpp/src/utils.h b/cpp/src/utils.h index 626ec4a..cd4ae1a 100644 --- a/cpp/src/utils.h +++ b/cpp/src/utils.h @@ -1,172 +1,160 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * utils.cpp * * Author: pgess */ #ifndef UTILS_H #define UTILS_H -#include "jeayeson/jeayeson.hpp" +#include +#include namespace xreate { template struct AddTag { explicit AddTag(const Source &src) : __src(src) { } explicit AddTag(Source &&src) : __src(std::move(src)) { } operator const Source&() const{ return __src; } const Source& get() const{ return __src; } const Source* operator->() const { return &__src; } private: Source __src; }; struct Expand_t{}; template using Expanded = AddTag; -//DEBT move to resources compiler. https://github.com/markusfisch/cpprc -class Config { -private: - json_map __storage; - static Config __self; - Config(); - -public: - static std::string get(std::string key) { - return __self.__storage.get_for_path(key).get(); - } -}; - /** \brief Decorators support */ template struct DecoratorsDict{ //typedef ConcreteDecoratorForTag result; }; template struct Decorators{ typedef typename DecoratorsDict::result Instance; template static Instance* getInterface(Base* obj){ Instance* result = dynamic_cast< Instance* > (obj); assert(result); return result; } }; template struct ManagedPtr { static ManagedPtr Invalid() { return ManagedPtr(); } ManagedPtr() : __storage(0) { } ManagedPtr(unsigned int id, const std::vector* storage) : __id(id), __storage(storage) { } Target& operator*() const { assert(isValid() && "Invalid Ptr"); return *__storage->at(__id); } void operator=(const ManagedPtr& other) { __id = other.__id; __storage = other.__storage; } bool operator==(const ManagedPtr& other) { return isValid() && (__id == other.__id); } Target* operator->() const noexcept { assert(isValid() && "Invalid Ptr"); return __storage->at(__id); } inline bool isValid() const { return (__storage) && (0 <= __id) && (__id < __storage->size()); } inline operator bool() const { return isValid(); } ManagedPtr& operator++() { ++__id; return *this; } inline unsigned int id() const { return __id; } bool operator< (const ManagedPtr& other) const{ if(__storage != other.__storage) return __storage < other.__storage; return __id < other.__id; } private: unsigned int __id = 0; const std::vector * __storage = 0; }; } std::wstring utf8_to_wstring(const std::string& str); std::string wstring_to_utf8(const std::wstring& str); #define RST "\x1B[0m" #define KRED "\x1B[31m" #define KGRN "\x1B[32m" #define KYEL "\x1B[33m" #define KBLU "\x1B[34m" #define KMAG "\x1B[35m" #define KCYN "\x1B[36m" #define KWHT "\x1B[37m" #define FRED(x) KRED << x << RST #define FGRN(x) KGRN < * Created on July 3, 2017, 6:03 PM */ #include "xreatemanager.h" -#include "pass/abstractpass.h" +#include "pass/ipass.h" #include "aux/transcend-decorators.h" #include "aux/xreatemanager-decorators.h" #include "llvmlayer.h" + #include #include namespace xreate { void PassManager::registerPass(IPass* pass, const PassId& id, IPass* parent) { __passes.emplace(id, pass); __passDependencies.emplace(parent, pass); } IPass* PassManager::getPassById(const PassId& id){ assert(__passes.count(id)); return __passes[id]; } bool PassManager::isPassRegistered(const PassId& id){ return __passes.count(id); } void PassManager::executePasses(){ std::list passes{nullptr}; while (passes.size()){ IPass* parent = passes.front(); auto range = __passDependencies.equal_range(parent); for (auto i=range.first; i!=range.second; ++i){ IPass* pass = i->second; pass->run(); pass->finish(); passes.push_back(pass); } passes.pop_front(); } } void PassManager::prepare(AST* ast){ root = ast; transcend = new DefaultTranscendLayerImpl(); transcend->ast = ast; llvm = new LLVMLayer(ast); } PassManager::~PassManager(){} typedef XreateManagerDecoratorFull XreateManagerDecoratorDefault; namespace details{ namespace tier2{ XreateManager* XreateManager::prepare(std::string&& code){ auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } }} namespace details { namespace tier1 { XreateManager* XreateManager::prepare(std::string&& code){ auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } }} XreateManager* XreateManager::prepare(std::string&& code) { auto man = new XreateManagerImpl; man->prepareCode(std::move(code)); return man; } XreateManager* XreateManager::prepare(FILE* code){ auto man = new XreateManagerImpl; man->prepareCode(code); return man; } } /** * \class xreate::XreateManager * \brief Entry point of Xreate API * * Manages whole Xreate compiler's internal workflow. There are 4 distinctive stages managed by XreateManager: * - `initPasses()` To init passes. * - `executePasses()` To execute passes. * - `analyse()` To run the reasoner. * - `run()` To run the compiler. * * * \section xm_adapt Adaptability * For adaptability reasons XreateManager comes with several *frontends*: * - `xreate::details::tier2::XreateManager` exposes all the stages to clients for full control. * - `xreate::details::tier1::XreateManager` exposes `analyse()` along with `run()`, where `analyse()` combines execution of all previous stages. * - `xreate::XreateManager` exposes `run()` only which properly initializes and executes all the stages for convenient use. * * Besides, there are also *backends* as follows: * - \ref XreateManagerDecoratorBase Simple backend intended for inheritance, does not provide much functionality. * - \ref XreateManagerDecoratorFull Backend intended to initialize all builtin passes. * * Thus, client could combine a desired frontend along with a desired backend as it sees fit. * Default xreate::XreateManager wraps around the full backend to init all builtin passes, and * the simplest frontend with the only `run()` exposed to execute all stages at once. */ \ No newline at end of file diff --git a/cpp/src/xreatemanager.h b/cpp/src/xreatemanager.h index 1d2f8ad..4289e79 100644 --- a/cpp/src/xreatemanager.h +++ b/cpp/src/xreatemanager.h @@ -1,145 +1,146 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * xreatemanager.h * * Author: pgess * Created on July 3, 2017, 6:03 PM */ /** * \file * \brief The entry point of Xreate API. */ #ifndef PASSMANAGER_H #define PASSMANAGER_H #include #include //stdio external struct _IO_FILE; typedef struct _IO_FILE FILE; namespace xreate { namespace grammar { namespace main { class Scanner; }}} namespace xreate { class IPass; class TranscendLayer; class LLVMLayer; class AST; enum class PassId { CFAPass, CFATemporalSeqPass, CompilePass, DFAPass, EnvironmentTestsPass, LoggerPass, RulesPass, - InterpretationPass, - VersionsPass + InterpretationPass, + VersionsPass, + TranscendPass }; /** * \class PassManager * \brief The base class to control passes */ class PassManager{ public: void prepare(AST* ast); void registerPass(IPass* pass, const PassId& id, IPass* prerequisite=nullptr); IPass* getPassById(const PassId& id); bool isPassRegistered(const PassId& id); void executePasses(); virtual ~PassManager(); TranscendLayer* transcend; LLVMLayer* llvm; AST* root; private: std::map __passes; std::multimap __passDependencies; }; namespace details{ namespace tier2{ class XreateManager: public virtual PassManager{ public: virtual ~XreateManager(){}; virtual void initPasses()=0; // virtual void executePasses()=0; virtual void analyse()=0; virtual void* run()=0; static XreateManager* prepare(std::string&& code); static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public Decorator { public: }; }} //namespace details::tier2 namespace details{ namespace tier1{ class XreateManager: public virtual PassManager{ public: virtual void analyse()=0; virtual void* run()=0; static XreateManager* prepare(std::string&& code); static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public XreateManager, public details::tier2::XreateManagerImpl { typedef details::tier2::XreateManagerImpl PARENT; public: void analyse(){ PARENT::initPasses(); PARENT::executePasses(); PARENT::analyse(); } void* run(){ return PARENT::run(); } }; }} //namespace details::tier1 class XreateManager: public virtual PassManager{ public: /** \brief Consequently executes all compilation and code execution phases */ virtual void* run()=0; /** \brief Constructs XreateManager for a given code */ static XreateManager* prepare(std::string&& code); /** \brief Constructs XreateManager for a given script file */ static XreateManager* prepare(FILE* code); }; template class XreateManagerImpl: public XreateManager, public details::tier1::XreateManagerImpl{ typedef details::tier1::XreateManagerImpl PARENT; public: void* run(){ PARENT::analyse(); return PARENT::run(); } }; } //namespace xreate #endif diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 188831b..4272d95 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,61 +1,26 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate-tests) find_package(GTest REQUIRED) INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS}) INCLUDE_DIRECTORIES("/usr/include/libxml2") INCLUDE_DIRECTORIES($) - +INCLUDE_DIRECTORIES("${CMAKE_HOME_DIRECTORY}/../vendors/jeayeson/include/") # TESTS #========================= FIND_PACKAGE (LLVM REQUIRED) message("LLVM_LIBRARY_DIRS: " ${LLVM_LIBRARY_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) set (LIBCLASP_PATH ${CLINGO_PATH}/build/debug) link_directories(${LIBCLASP_PATH}) #aux_source_directory(. TEST_FILES) -set(TEST_FILES - universal.cpp - introduction.cpp - unit-test-example.cpp - transcend-ast.cpp - supplemental/docutils - latetranscend.cpp - cfa.cpp - latex.cpp - polymorph.cpp - transcend.cpp - virtualization.cpp - exploitation.cpp - effects-communication.cpp - association.cpp - main.cpp - modules.cpp - attachments.cpp - ast.cpp - dfa.cpp - compilation.cpp - ExpressionSerializer.cpp - externc.cpp - types.cpp - #vendorsAPI/clangAPI.cpp - #vendorsAPI/xml2.cpp - #vendorsAPI/json.cpp - - containers.cpp - interpretation.cpp - loops.cpp - - #supplemental/versions-algorithm-data_dependency.cpp - effects-versions.cpp -) -add_executable(${PROJECT_NAME} ${TEST_FILES}) +add_executable(${PROJECT_NAME} ${XREATE_TEST_FILES}) target_link_libraries(${PROJECT_NAME} xreate ${GTEST_LIBRARIES} pthread xml2 gcov) add_custom_target (coverage COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/code-coverage.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cpp/tests/association.cpp b/cpp/tests/association.cpp index df36c38..9370a64 100644 --- a/cpp/tests/association.cpp +++ b/cpp/tests/association.cpp @@ -1,138 +1,139 @@ /* 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. + AtomNumT = type slave("atomNumT"). + AtomStrT = type slave("atomStrT"). + CompListT = type slave("compListT"). + CompArithT = type slave("compArithT"). - test = function:: num; entry + test = function:: int; 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 + test = function:: int; 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 + test = function:: int; 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 + test = function:: int; 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"); - + + std::cout << code << std::endl; 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 15c70c9..2f76ecf 100644 --- a/cpp/tests/ast.cpp +++ b/cpp/tests/ast.cpp @@ -1,264 +1,288 @@ /* 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 "supplemental/basics.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, IdentHyphen1){ XreateManager* man = XreateManager::prepare(R"Code( my-fn = function(m-n:: num):: num; entry { b = m-n-1:: int. 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, TypeVariantEmpty){ + std::string code = R"( + my-rec-t = type variant{} + )"; + + ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 29: Variant type can't be empty."); +} + 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")); 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_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); -} - TEST(AST, Doc_Versions1){ string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_1"); string code_Variants2 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "Versions1_2"); string code = R"Code( test = function:: int; entry { y })Code"; { + std::cout << code_Variants1 << std::endl; XreateManager* man = XreateManager::prepare(move(code_Variants1)); man->run(); delete man; ASSERT_TRUE(true); } - { - replace(code, "", code_Variants2); - auto man = details::tier1::XreateManager::prepare(move(code)); - ASSERT_DEATH(man->analyse(), ".*versions graph.*"); - } +// { +// replace(code, "", code_Variants2); +// auto man = details::tier1::XreateManager::prepare(move(code)); +// ASSERT_DEATH(man->analyse(), ".*versions graph.*"); +// } +} + +TEST(AST, Intrinsics1){ + string code = R"Code( +test = function:: [int] +{ + intrinsic array_init(8):: [int] +})Code"; + + XreateManager* man = XreateManager::prepare(move(code)); + const Expression bodyE = man->root->findFunction("test")->getEntryScope()->getBody(); + + ASSERT_EQ(Operator::CALL_INTRINSIC, bodyE.op); + ASSERT_EQ(IntrinsicFn ::ARR_INIT, (IntrinsicFn) bodyE.getValueDouble()); +} + +TEST(AST, TypeRecordEmpty){ + std::string code = R"( + my-rec-t = type {} + )"; + + ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 22: Record type can't be empty."); +} + +TEST(AST, PredPredicates1){ + string code = R"( + my-fn = function:: int; entry() {0} + )"; + + auto man = XreateManager::prepare(move(code)); } \ No newline at end of file diff --git a/cpp/tests/cfa.cpp b/cpp/tests/cfa.cpp index b15a9e1..1f1bee9 100644 --- a/cpp/tests/cfa.cpp +++ b/cpp/tests/cfa.cpp @@ -1,193 +1,193 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * cfa.cpp * * Created on: Jul 17, 2015 * Author: pgess */ #include "xreatemanager.h" #include "pass/dfapass.h" #include "pass/cfapass.h" #include "analysis/cfagraph.h" #include "pass/cfatemporalseqpass.h" #include "analysis/temporalseqgraph.h" #include "pass/compilepass.h" -#include "compilation/scopedecorators.h" +#include "compilation/decorators.h" #include "gtest/gtest.h" #include "aux/xreatemanager-decorators.h" #include #include #include using namespace xreate; using namespace xreate::cfa; using namespace std; TEST(CFA, testFunctionAnnotations) { string&& program = "f2 = function::int; annotationF2 {\n" " 0\n" "}\n" "\n" "f1 = function:: int; entry; annotationF1 {\n" " f2() + 10\n" "}"; details::tier1::XreateManager* man = details::tier1::XreateManager::prepare(move(program)); man->analyse(); StaticModel answer = man->transcend->query("annotationF1"); EXPECT_EQ(1, answer.size()); answer = man->transcend->query("annotationF2"); EXPECT_EQ(1, answer.size()); } TEST(CFA, testLoopContextExists) { details::tier1::XreateManager* man = details::tier1::XreateManager::prepare ( "interface(cfa){\n" " operator fold:: annotation1.\n" "}\n" "\n" "main = function:: int; entry {\n" " x = [1..10]:: [int].\n" " sum = loop fold (x->el:: int, 0->sum):: int {\n" " el + sum + f1()\n" " }. \n" " sum\n" "}" "guard:: annotation1 {" " f1 = function::int {\n" " x = 0:: int. " " x\n" " }" "}" ); man->analyse(); StaticModel model = man->transcend->query("annotation1"); ScopePacked scopeIdActual = std::get<0>(TranscendLayer::parse(model.begin()->second)); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); const Expression& exprSum = scopeEntry->getDefinition(scopeEntry->getSymbol("sum")); CodeScope* scopeExpected = exprSum.blocks.front(); ScopePacked scopeIdExpected = man->transcend->pack(scopeExpected); ASSERT_EQ(scopeIdExpected, scopeIdActual); } TEST(CFA, TemporalFnCall) { details::tier2::XreateManager* man = details::tier2::XreateManager::prepare( R"Code( a = function::int{ seq {x = b():: int. x} {x = 0 :: int. x}::int } b = function::int {y = 0. y} )Code"); CodeScope* scopeSeq1 = man->root->findFunction("a")->getEntryScope()->getBody().blocks.front(); CodeScope* scopeSeq2 = *(++man->root->findFunction("a")->getEntryScope()->getBody().blocks.begin()); CodeScope* scopeB = man->root->findFunction("b")->getEntryScope(); CodeScope* scopeA = man->root->findFunction("a")->getEntryScope(); ScopePacked psSeq1 = man->transcend->pack(scopeSeq1); ScopePacked psSeq2 = man->transcend->pack(scopeSeq2); ScopePacked psB = man->transcend->pack(scopeB); ScopePacked psA = man->transcend->pack(scopeA); CFATemporalSeqPass* pass = new CFATemporalSeqPass(man); man->registerPass(pass, PassId::CFATemporalSeqPass); man->executePasses(); const TemporalSeqGraph* report = dynamic_cast ( man->getPassById(PassId::CFATemporalSeqPass))->getReport(); delete pass; std::ostringstream output; report->print(output); cout << output.str() << endl; ASSERT_TRUE(report->isOrdered(psSeq2, psSeq1)); //ASSERT_TRUE(report->isOrdered(psSeq2, psB)); ASSERT_FALSE(report->isOrdered(psSeq1, psA)); } TEST(CFA, TemporalChildScope) { details::tier2::XreateManager* man = details::tier2::XreateManager::prepare( R"Code( a = function::int{ seq {x = 0:: int. x} {x=0::int. if(x>0)::int{1} else {0}}::int } )Code"); CodeScope* scopeSeq1 = man->root->findFunction("a")->getEntryScope()->getBody().blocks.front(); CodeScope* scopeSeq2 = *(++man->root->findFunction("a")->getEntryScope()->getBody().blocks.begin()); CodeScope* scopeIf1 = scopeSeq2->getBody().blocks.front(); CodeScope* scopeIf2 = *(++scopeSeq2->getBody().blocks.begin()); ScopePacked psSeq1 = man->transcend->pack(scopeSeq1); ScopePacked psSeq2 = man->transcend->pack(scopeSeq2); ScopePacked psIf1 = man->transcend->pack(scopeIf1); ScopePacked psIf2 = man->transcend->pack(scopeIf2); CFATemporalSeqPass* pass = new CFATemporalSeqPass(man); man->registerPass(pass, PassId::CFATemporalSeqPass); man->executePasses(); const TemporalSeqGraph* report = dynamic_cast ( man->getPassById(PassId::CFATemporalSeqPass))->getReport(); delete pass; std::ostringstream output; report->print(output); cout << output.str() << endl; ASSERT_TRUE(report->isOrdered(psSeq2, psSeq1)); ASSERT_TRUE(report->isOrdered(psIf1, psSeq1)); ASSERT_TRUE(report->isOrdered(psIf2, psSeq1)); ASSERT_FALSE(report->isOrdered(psIf2, psSeq2)); } TEST(CFA, Temporal_Payload_PathCollapse_1){ details::tier2::XreateManager* man = details::tier2::XreateManager::prepare( R"Code( import raw("scripts/cfa/payload.lp"). b = function::int {d()} c = function::int {d()} d = function::int {0} a = function:: int { seq {context:: test. 0} {b() + c()} } )Code"); string script = R"Code( cfa_payload(P, scope(Scope), ()):- bind_scope(Scope, P, strong). )Code"; CFATemporalSeqPass* pass = new CFATemporalSeqPass(man); man->registerPass(pass, PassId::CFATemporalSeqPass); man->registerPass(new CFAPass(man), PassId::CFAPass); man->executePasses(); const TemporalSeqGraph* report = dynamic_cast ( man->getPassById(PassId::CFATemporalSeqPass))->getReport(); man->transcend->addRawScript(move(script)); testing::internal::CaptureStdout(); man->analyse(); std::string outputActual = testing::internal::GetCapturedStdout(); cout << outputActual << endl; string outputExpected = "cfa_payload(test,scope(2),())"; ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); delete pass; } \ No newline at end of file diff --git a/cpp/tests/compilation.cpp b/cpp/tests/compilation.cpp index f098e2f..d44f2cf 100644 --- a/cpp/tests/compilation.cpp +++ b/cpp/tests/compilation.cpp @@ -1,243 +1,278 @@ /* 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 "supplemental/basics.h" +#include "llvmlayer.h" #include "gtest/gtest.h" using namespace xreate; +using namespace std; //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}. + global = type predicate { + entry + } Command= type variant{ - Add::TwoArguments, - Dec::OneArgument + Add(x::int, y::int), + Dec(x::int) }. - main = function::Command; entry { - Dec({x=2})::Command + main = function::Command; entry() { + Dec(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 + Add(x::int, y::int), + Dec(x::int) }. main = function::int; entry { - //command = Dec({x = 8}):: Command. - command = Add({x= 3, y= 5}):: Command. + command = Add(3, 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, + Add(x::int, y::int), Dec }. main = function(arg::int):: int; entry { - command = if (arg > 0)::Command {Dec()} else {Add({x=1,y=2})}. + command = if (arg > 0)::Command {Dec()} else {Add(1, 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(); + Fn2Args startFn = (Fn2Args) man->run(); +} + +TEST(Compilation, StructIndex1){ + std::string code = + R"Code( +Anns = type predicate { + entry() +} +test = function:: int; entry() +{ + x = {a = ({b = 3}::{b:: int})}:: {a:: {b:: int}}. + 2 + x["a", "b"] + x["a"]["b"] +} +)Code"; + + std::unique_ptr man(XreateManager::prepare(move(code))); + FnNoArgs startFn = (FnNoArgs) man->run(); + int result = startFn(); + ASSERT_EQ(2, result); +} + +TEST(Compilation, PreferredInt1){ + std::unique_ptr man(XreateManager::prepare("")); + TypesHelper utils(man->llvm); + int bitwidth = utils.getPreferredIntTy()->getBitWidth(); + ASSERT_EQ(64, bitwidth); +} + +TEST(Compilation, PredPredicates1){ + string code = R"( + my-fn = function:: int; entry() {0} + )"; + + auto man = XreateManager::prepare(move(code)); + FnNoArgs startFn = (FnNoArgs) man->run(); + int result = startFn(); + ASSERT_EQ(0, result); } \ No newline at end of file diff --git a/cpp/tests/containers.cpp b/cpp/tests/containers.cpp index 0e7482d..a7123c0 100644 --- a/cpp/tests/containers.cpp +++ b/cpp/tests/containers.cpp @@ -1,155 +1,299 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * containers.cpp * * Created on: Jun 9, 2015 * Author: pgess */ #include "xreatemanager.h" #include "query/containers.h" #include "main/Parser.h" + #include "supplemental/docutils.h" +#include "supplemental/basics.h" #include "gtest/gtest.h" using namespace std; using namespace xreate::grammar::main; using namespace xreate::containers; using namespace xreate; -typedef int (*IntFn)(); +TEST(Containers, AST_List1){ + string code = R"( + my-test = function:: int { + {month = 1, 2, #3 = 8} + } + )"; -TEST(Containers, ListAsArray){ - XreateManager* man = XreateManager::prepare( + auto man = XreateManager::prepare(move(code)); + const Expression& listE = man->root->findFunction("my-test")->getEntryScope()->getBody(); + ASSERT_EQ(3, listE.bindings.size()); + ASSERT_EQ(3, listE.operands.size()); + set fields; + fields.insert(listE.bindings.begin(), listE.bindings.end()); -R"Code( - main = function(x:: int):: int;entry { - a = {1, 2, 3}:: [int]. - a[x] + ASSERT_TRUE(fields.count("month")); + ASSERT_TRUE(fields.count("0")); + ASSERT_TRUE(fields.count("3")); +} + +TEST(AST, AST_ListIndex1){ + string code = R"( + my-fn = function:: int; entry() + { + x = [1..2]:: [int]. + x + {[1] = 1} } -)Code" ); + )"; + + auto man = XreateManager::prepare(move(code)); + const Expression& bodyE = man->root->findFunction("my-fn")->getEntryScope()->getBody(); + + ASSERT_EQ(2, bodyE.operands.size()); + const Expression opLiInE = bodyE.operands.at(1); + ASSERT_EQ(2, opLiInE.operands.size()); +} + +TEST(Containers, DONE_RecInitByList1){ +string code = R"( + Rec = type {x:: int, y:: int}. + test = function(a:: int, b::int):: Rec; entry() { + {x = a + b, y = 2} + } +)"; + + auto man = XreateManager::prepare(move(code)); + man->run(); +} + +TEST(Containers, DONE_RecInitByList2){ + string code = R"( + Rec = type {x:: int, y:: int}. + test = function(a:: int, b::int):: Rec; entry() { + {a + b, y = 2} + } +)"; + + auto man = XreateManager::prepare(move(code)); + man->run(); +} + +TEST(Containers, DONE_RecUpdateByList1){ + string code = R"( + Rec = type {x:: int, y:: int}. + test = function(a:: int, b::int):: Rec; entry() { + r = {0, y = 2}:: Rec. + + r : {a + b} + } +)"; + + auto man = XreateManager::prepare(move(code)); + man->run(); +} + +TEST(Containers, DONE_RecUpdateByListIndex1){ + string code = R"( + Rec = type {x:: int, y:: int}. + test = function(a:: int, b::int):: int; entry() { + r1 = undef:: Rec. + r2 = r1 : {[1] = b, [0] = a}:: Rec. + + r2["x"] + } +)"; + + auto man = XreateManager::prepare(move(code)); + Fn2Args program = (Fn2Args) man->run(); + ASSERT_EQ(10, program(10, 11)); +} + +TEST(Containers, DONE_RecUpdateInLoop1){ + FILE* code = fopen("scripts/containers/RecUpdateInLoop1.xreate", "r"); + assert(code != nullptr); + + auto man = XreateManager::prepare(code); + Fn1Args program = (Fn1Args) man->run(); + ASSERT_EQ(11, program(10)); +} + +TEST(Containers, DONE_ArrayInit1){ + XreateManager* man = XreateManager::prepare( +R"Code( +main = function(x:: int):: int; entry() { + a = {1, 2, 3}:: [int]. + a[x] +} +)Code"); void* mainPtr = man->run(); - int (*main)(int) = (int (*)(int))mainPtr; + Fn1Args main = (Fn1Args) mainPtr; ASSERT_EQ(2, main(1)); delete man; } +TEST(Containers, DONE_ArrayUpdate1){ + XreateManager* man = XreateManager::prepare(R"( +main = function(x::int):: int; entry() +{ + a = {1, 2, 3}:: [int]; csize(5). + b = a : {[1] = x}:: [int]; csize(5). + b[1] +} +)"); + + void* mainPtr = man->run(); + Fn1Args main = (Fn1Args) mainPtr; + ASSERT_EQ(2, main(2)); + + delete man; +} + TEST(Containers, ListAsArray2){ XreateManager* man = XreateManager::prepare( R"Code( // CONTAINERS import raw("scripts/dfa/ast-attachments.lp"). import raw("scripts/containers/containers.lp"). main = function:: int;entry { a= {1, 2, 3}:: [int]. b= loop map(a->el:: int):: [int]{ 2 * el }. sum = loop fold(b->el:: int, 0->acc):: int { acc + el }. sum } )Code"); void* mainPtr = man->run(); - int (*main)() = (int (*)())mainPtr; + FnNoArgs main = (FnNoArgs) mainPtr; ASSERT_EQ(12, main()); delete man; } +TEST(Containers, Doc_RecField1){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecField1"); + XreateManager::prepare(move(code_Variants1)); + + ASSERT_TRUE(true); +} + +TEST(Containers, Doc_RecUpdate1){ + string code_Variants1 = getDocumentationExampleById("documentation/Syntax/syntax.xml", "RecUpdate1"); + XreateManager::prepare(move(code_Variants1)); + + ASSERT_TRUE(true); +} + TEST(Containers, ContanierLinkedList1){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); const Symbol symb_chilrenRaw{body->getSymbol("childrenRaw"), body}; containers::ImplementationLinkedList iLL(symb_chilrenRaw); ASSERT_EQ(true, static_cast(iLL)); ASSERT_EQ("next", iLL.fieldPointer); Implementation impl = Implementation::create(symb_chilrenRaw); ASSERT_NO_FATAL_FAILURE(impl.extract()); ImplementationRec recOnthefly = impl.extract(); ASSERT_EQ(symb_chilrenRaw, recOnthefly.source); } TEST(Containers, Implementation_LinkedListFull){ FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate","r"); assert(input != nullptr); std::unique_ptr program(XreateManager::prepare(input)); void* mainPtr = program->run(); int (*main)() = (int (*)())(intptr_t)mainPtr; - int answer = main(); + intmax_t answer = main(); ASSERT_EQ(17, answer); fclose(input); } TEST(Containers, Doc_Intr_1){ string example = R"Code( import raw("scripts/containers/containers.lp"). test = function:: int; entry { x } )Code"; string body = getDocumentationExampleById("documentation/Concepts/containers.xml", "Intr_1"); replace(example, "", body); XreateManager* xreate = XreateManager::prepare(move(example)); - IntFn program = (IntFn) xreate->run(); - - int result = program(); + FnNoArgs program = (FnNoArgs) xreate->run(); + + intmax_t result = program(); ASSERT_EQ(1, result); } TEST(Containers, Doc_OpAccessSeq_1){ string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessSeq_1"); XreateManager* xreate = XreateManager::prepare(move(example)); - IntFn program = (IntFn) xreate->run(); - - int result = program(); + FnNoArgs program = (FnNoArgs) xreate->run(); + + intmax_t result = program(); ASSERT_EQ(15, result); } TEST(Containers, Doc_OpAccessRand_1){ string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "OpAccessRand_1"); XreateManager* xreate = XreateManager::prepare(move(example)); - IntFn program = (IntFn) xreate->run(); - - int result = program(); + FnNoArgs program = (FnNoArgs) xreate->run(); + + intmax_t result = program(); ASSERT_EQ(2, result); } TEST(Containers, Doc_ASTAttach_1){ string example = getDocumentationExampleById("documentation/Concepts/containers.xml", "ASTAttach_1"); string outputExpected = "containers_impl(s(1,-2,0),onthefly)"; XreateManager* xreate = XreateManager::prepare(move(example)); testing::internal::CaptureStdout(); xreate->run(); std::string outputActual = testing::internal::GetCapturedStdout(); ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); } + +TEST(Containers, IntrinsicArrInit1){ + XreateManager* man = XreateManager::prepare( + +R"Code( +FnAnns = type predicate { + entry +} + +main = function(x:: int):: int; entry() { + a{0} = intrinsic array_init(16):: [int]. + a{1} = a{0} + {15: 12} +} +)Code"); +} diff --git a/cpp/tests/dimensions.cpp b/cpp/tests/dimensions.cpp new file mode 100644 index 0000000..60e41c8 --- /dev/null +++ b/cpp/tests/dimensions.cpp @@ -0,0 +1,190 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * ast.cpp + * + * Created on: Jan, 2020 + * Author: pgess + */ + +#include "xreatemanager.h" +#include "analysis/transcendtarget.h" +#include "gtest/gtest.h" + +using namespace std; +using namespace xreate; + +TEST(Dimensions, AST_AnnotationTypeEmpty){ + std::string code = R"( + my-rec-t = type predicate{} + )"; + + ASSERT_DEATH(XreateManager::prepare(move(code)), "-- line 2 col 31: Predicate type can't be empty."); +} + +TEST(Dimensions, AST_RecognizeAnnIdents){ + std::string code = R"( + my-func1 = function:: int; UnknownAnn() {0} + my-func2 = function:: int; UnknownIdent {0} + )"; + + ASSERT_DEATH(XreateManager::prepare(move(code)), + "-- line 2 col 32: Undefined predicate\n-- line 3 col 32: Undefined scope"); +} + +TEST(Dimensions, CompileFnAnn1){ + std::string code = R"( + FnAnns = type predicate{ + FnMyAnn + }. + myf = function:: int; FnMyAnn() { + 0 + } + )"; + + auto xreate = details::tier2::XreateManager::prepare(move(code)); + xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass); + xreate->executePasses(); + testing::internal::CaptureStdout(); + xreate->analyse(); + std::string outputActual = testing::internal::GetCapturedStdout(); + cout << outputActual; + + string outputExpected = "bind_func(\"myf\",fnMyAnn)"; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Dimensions, CompileScopeAnn1){ + std::string code = R"( + ScopeAnns = type predicate{ + MyScopeAnn + }. + + myf = function:: int { + context:: MyScopeAnn(). + 0 + } + )"; + + auto xreate = details::tier2::XreateManager::prepare(move(code)); + xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass); + xreate->executePasses(); + testing::internal::CaptureStdout(); + xreate->analyse(); + std::string outputActual = testing::internal::GetCapturedStdout(); + cout << outputActual; + string outputExpected = "bind_scope(0, myScopeAnn)"; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Dimensions, CompileScopeVar1){ + std::string code = R"( + ScopeAnns = type predicate{ + MyScopeAnn(int) + }. + + myf = function:: int { + x = MyScopeAnn(8):: ScopeAnns. + if (0>2):: int + { + context:: x. + 0 + } else {0} + } + )"; + + auto xreate = details::tier2::XreateManager::prepare(move(code)); + xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass); + xreate->executePasses(); + testing::internal::CaptureStdout(); + xreate->analyse(); + std::string outputActual = testing::internal::GetCapturedStdout(); + cout << outputActual; + string outputExpected = "bind_scope(1,myScopeAnn(8))"; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Dimensions, RepresentDeepAnn1){ + std::string code = R"( +figure = type predicate { + circle(int) +} + +fn-ann = type predicate { + entry, demand(figure) +} + +dem-fn = function:: int {0} + +main-fn = function(x::int, y::int):: int; entry() { + fig = circle(x+y):: figure. + dem-fn():: int; demand(fig) +} + )"; + + auto xreate = details::tier2::XreateManager::prepare(move(code)); + xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass); + xreate->executePasses(); + testing::internal::CaptureStdout(); + xreate->analyse(); + std::string outputActual = testing::internal::GetCapturedStdout(); + string outputExpected = "bind(s(0,-2,1),demand(circle(a(41))))"; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Dimensions, CompileDeepAnn1){ + std::string code = R"( +figure = type predicate { + oval, circle(int), square +} + +fn-ann = type predicate { + entry, demand(figure) +} + +fn-dem = function:: int {0} + +main-fn = function(x::int, y::int):: int; entry() { + fig = circle(x+y):: figure. + fn-dem():: int; demand(fig) +} + )"; + + auto xreate = details::tier2::XreateManager::prepare(move(code)); + xreate->transcend->addRawScript(R"( + func_demand("fn-dem", "figf", "figure"). + func_supply(Site, "figf", Value):- bind(Site, demand(Value)). + )"); + + xreate->registerPass(new TranscendPass(xreate), PassId::TranscendPass); + xreate->executePasses(); + //testing::internal::CaptureStdout(); + xreate->analyse(); + xreate->run(); + + + //std::string outputActual = testing::internal::GetCapturedStdout(); + //string outputExpected = "bind(s(0,-2,1),demand(circle(a(41))))"; + //ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Dimensions, DISABLED_Test1){ + string s = +R"( + my-ann = type annotation { + circle, + square + } + + my-func = function:: int; entry + { + x = circle():: my-ann. + + context:: x + { + 0 + } + } +)"; + cout << s; +} \ No newline at end of file diff --git a/cpp/tests/exploitation.cpp b/cpp/tests/exploitation.cpp index 8316438..d0ba5fd 100644 --- a/cpp/tests/exploitation.cpp +++ b/cpp/tests/exploitation.cpp @@ -1,63 +1,64 @@ /* 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 "supplemental/basics.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); - + replace(code, "", codeSpecializations); + + std::cout << code << std::endl; 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/interpretation.cpp b/cpp/tests/interpretation.cpp index 4609b15..a833453 100644 --- a/cpp/tests/interpretation.cpp +++ b/cpp/tests/interpretation.cpp @@ -1,485 +1,476 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * interpretation.cpp * * Created on: - * Author: pgess */ #include "attachments.h" using namespace xreate; #include "xreatemanager.h" #include "compilation/targetinterpretation.h" -#include "analysis/interpretation.h" #include "supplemental/docutils.h" #include "gtest/gtest.h" #include "boost/scoped_ptr.hpp" //#define FRIENDS_INTERPRETATION_TESTS \ // friend class ::Modules_AST2_Test; \ // friend class ::Modules_Discovery1_Test; \ // friend class ::Modules_Solve1_Test; #include "pass/interpretationpass.h" using namespace xreate::grammar::main; using namespace xreate::interpretation; using namespace std; TEST(Interpretation, Analysis_StatementIF_1) { XreateManager* man = XreateManager::prepare( R"Code( main = function::bool { x = "a":: string. - y = if (x=="b"):: bool; i12n(on) { + y = if (x=="b"):: bool; i12n(on()) { true } else { false }. y } )Code"); InterpretationPass* pass = new InterpretationPass(man); pass->run(); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); Symbol symbolY{scopeEntry->getSymbol("y"), scopeEntry}; InterpretationData &dataSymbolY = Attachments::get(symbolY); ASSERT_EQ(INTR_ONLY, dataSymbolY.resolution); } TEST(Interpretation, Analysis_StatementIF_InterpretCondition_1) { XreateManager* man = XreateManager::prepare( R"Code( main = function(x:: int):: int { - comm= "inc":: string; i12n(on). + comm= "inc":: string; i12n(on()). y = if (comm == "inc")::int {x+1} else {x}. y } )Code"); InterpretationPass* pass = new InterpretationPass(man); pass->run(); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); Symbol symbolY{scopeEntry->getSymbol("y"), scopeEntry}; InterpretationData &dataSymbolY = Attachments::get(symbolY); ASSERT_EQ(CMPL_ONLY, dataSymbolY.resolution); ASSERT_EQ(IF_INTERPRET_CONDITION, dataSymbolY.op); } TEST(Interpretation, StatementCall_RecursionIndirect_1) { XreateManager* man = XreateManager::prepare( R"Code( searchNemo = function(data:: Data):: bool { if (data == "nemo"):: bool {false} else {searchDory(data)} } searchDory = function(data:: Data):: bool { if (data == "dory"):: bool {true} else {searchNemo(data)} } - entry = function:: bool; entry { - searchNemo(""):: bool; i12n(on) + entry = function:: bool; entry() { + searchNemo(""):: bool; i12n(on()) } )Code"); InterpretationPass* pass = new InterpretationPass(man); ASSERT_DEATH(pass->run(), "Indirect recursion detected"); } TEST(Interpretation, PartialIntr_1) { XreateManager* man = XreateManager::prepare( R"Code( - evaluate= function(argument:: num, code:: string; i12n(on)):: num { - switch(code):: num + evaluate= function(argument:: int, code:: string; i12n(on())):: int { + switch(code):: int case ("inc") {argument + 1} case ("dec") {argument - 1} case ("double") {argument * 2} case default {argument} } - main = function:: int; entry { - commands= {"inc", "double", "dec"}:: [string]; i12n(on). + main = function:: int; entry() { + commands= {"inc", "double", "dec"}:: [string]; i12n(on()). loop fold(commands->comm::string, 10->operand):: int { evaluate(operand, comm) } } )Code"); InterpretationPass* pass = new InterpretationPass(man); pass->run(); ManagedFnPtr fnEvaluate = man->root->findFunction("evaluate"); InterpretationResolution resFnEvaluate = pass->process(fnEvaluate); ASSERT_EQ(CMPL_ONLY, resFnEvaluate); ASSERT_TRUE(FunctionInterpretationHelper::needPartialInterpretation(fnEvaluate)); const Expression &exprLoop = man->root->findFunction("main")->__entry->getBody(); Symbol symbCallEv{{0, versions::VERSION_NONE}, exprLoop.blocks.front()}; InterpretationData dataCallEv = Attachments::get(symbCallEv); ASSERT_EQ(CMPL_ONLY, dataCallEv.resolution); ASSERT_EQ(CALL_INTERPRET_PARTIAL, dataCallEv.op); } TEST(Interpretation, PartialIntr_3) { xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( R"Code( Command= type variant {INC, DEC, DOUBLE}. - evaluate= function(argument:: num, code:: Command; i12n(on)):: num { + evaluate= function(argument:: int, code:: Command; i12n(on())):: int { switch variant(code)::int case (INC) {argument + 1} case (DEC) {argument - 1} case (DOUBLE) {argument * 2} } - main = function::int; entry { - commands= {INC(), DOUBLE(), DEC()}:: [Command]; i12n(on). + main = function::int; entry() { + commands= {INC(), DOUBLE(), DEC()}:: [Command]; i12n(on()). loop fold(commands->comm::Command, 10->operand):: int{ evaluate(operand, comm) } } )Code"); man->analyse(); if(!man->isPassRegistered(PassId::InterpretationPass)) { InterpretationPass* pass = new InterpretationPass(man); pass->run(); } int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(21, result); } TEST(Interpretation, PartialIntr_4) { xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( R"Code( Command= type variant {INC, DEC, DOUBLE}. - evaluate= function(argument:: num, code:: Command; i12n(on)):: num { - switch variant(code)::num + evaluate= function(argument:: int, code:: Command; i12n(on())):: int { + switch variant(code)::int case (INC) {argument + 1} case (DEC) {argument - 1} case (DOUBLE) {argument * 2} } - main = function::int; entry { + main = function::int; entry() { evaluate(4, DEC()) } )Code"); man->analyse(); if(!man->isPassRegistered(PassId::InterpretationPass)) { InterpretationPass* pass = new InterpretationPass(man); pass->run(); } int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(3, result); } TEST(Interpretation, SwitchVariant) { xreate::XreateManager* man = xreate::XreateManager::prepare( R"Code( -OneArgument = type{x::int}. -TWoArgument = type {x::int, y::int}. - Command= type variant { - ADD::TwoArguments, - DEC:: OneArgument, - DOUBLE::OneArgument + ADD(x::int, y::int), + DEC(x::int), + DOUBLE(x::int) }. -main = function::int; entry{ - program = ADD({x=2, y=3})::Command; i12n(on). +main = function::int; entry(){ + program = ADD(2, 3)::Command; i12n(on()). switch variant(program)::int case (ADD) {program["x"]+program["y"]} case (DEC) {1} case (DOUBLE) {2} } )Code"); int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(5, result); } TEST(Interpretation, SwitchVariantAlias) { xreate::XreateManager* man = xreate::XreateManager::prepare( R"Code( -OneArgument = type {x::int}. -TWoArgument = type {x::int, y::int}. - Command= type variant { - ADD::TwoArguments, - DEC:: OneArgument, - DOUBLE::OneArgument + ADD(x::int, y::int), + DEC(x::int), + DOUBLE(x::int) }. -main = function::int; entry{ - program = {ADD({x=2, y=3}), DEC({x=8})}::[Command]; i12n(on). +main = function::int; entry(){ + program = {ADD(2, 3), DEC(8)}::[Command]; i12n(on()). - switch variant(program[0]->program::Command)::int - case (ADD) {program["x"]+program["y"]} - case (DEC) {1} - case (DOUBLE) {2} + switch variant(program[0]->program::Command)::int + case (ADD) {program["x"]+program["y"]} + case (DEC) {1} + case (DOUBLE) {2} } )Code"); int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(5, result); } TEST(Interpretation, Multiindex1) { std::string script2 = R"Code( - extract = function(program::unknType)::int; i12n(on){ + extract = function(program::unknType)::int; i12n(on()){ program["arguments"][1] } - main = function::int; entry { - x = {arguments = {10, 9, 8, 7}}:: unknType; i12n(on). + main = function::int; entry() { + x = {arguments = {10, 9, 8, 7}}:: unknType; i12n(on()). extract(x) } )Code"; std::unique_ptr man(XreateManager::prepare(std::move(script2))); int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(9, result); } TEST(InterpretationExamples, Regexp1) { FILE* input = fopen("scripts/dsl/regexp.xreate", "r"); assert(input != nullptr); std::unique_ptr man(XreateManager::prepare(input)); int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(4, result); } TEST(Interpretation, Variant1) { std::string script = R"Code( -Data = type {Num:: [int], String::string}. - DataPacked = type variant { - Var1:: Data, - Var2:: Data + Var1(Num:: [int], String::string), + Var2(Num:: [int], String::string) }. extractInt = function(data::DataPacked):: int { - resultWrong = 0 :: int. + resultWrong = 0 :: int. - switch variant(data)::int - case (Var1) {data["Num", 0]} - case (Var2) {resultWrong} + switch variant(data)::int + case (Var1) {data["Num", 0]} + case (Var2) {resultWrong} } -main = function :: int; entry { - dataActual = 10. - dataPacked = Var1({Num = {dataActual}, String = "whatever"}):: DataPacked. +main = function :: int; entry() { + dataActual = {10}:: [int]. + dataPacked = Var1(dataActual, "whatever"):: DataPacked. - extractInt(dataPacked):: int; i12n(on) + extractInt(dataPacked):: int; i12n(on()) } )Code"; std::unique_ptr man(XreateManager::prepare(std::move(script))); int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(10, result); } TEST(Interpretation, Doc_Intr_1) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Intr_1"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } int (* main)() = (int (*)()) man->run(); int result = main(); ASSERT_EQ(0, result); } TEST(Interpretation, Doc_FnIntr_1) { string example1 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_1"); string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_FnIntr_1"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example1)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } unsigned char (* main)() = (unsigned char (*)()) man->run(); unsigned char result = main(); ASSERT_EQ(1, result); XreateManager* man2 = XreateManager::prepare(move(example2)); ASSERT_TRUE(true); } TEST(Interpretation, Doc_FnIntr_2) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_2"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } unsigned char (* main)() = (unsigned char (*)()) man->run(); unsigned char result = main(); ASSERT_EQ(1, result); } TEST(Interpretation, Doc_FnIntr_3) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "FnIntr_3"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } InterpretationResolution resolutionActual = pass->process(man->root->findFunction("unwrap")); ASSERT_EQ(ANY, resolutionActual); unsigned char (* main)() = (unsigned char (*)()) man->run(); unsigned char result = main(); ASSERT_NE(0, result); } TEST(Interpretation, Doc_LateIntr_1) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateIntr_1"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } int (* main)(int) = (int (*)(int)) man->run(); int result = main(1); ASSERT_EQ(2, result); } TEST(Interpretation, Doc_LateIntr_2) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateIntr_2"); string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_LateIntr_2"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); InterpretationPass* pass; if(man->isPassRegistered(PassId::InterpretationPass)) { pass = (InterpretationPass*) man->getPassById(PassId::InterpretationPass); } else { pass = new InterpretationPass(man); pass->run(); } const ManagedFnPtr &funcMain = man->root->findFunction("main"); InterpretationData &dataBody = Attachments::get(funcMain); ASSERT_EQ(FOLD_INTERPRET_INPUT, dataBody.op); int (* main)(int) = (int (*)(int)) man->run(); int result = main(10); ASSERT_EQ(21, result); XreateManager* man2 = XreateManager::prepare(move(example2)); ASSERT_TRUE(true); } TEST(Interpretation, Doc_LateFnIntr_1) { string example = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "LateFnIntr_1"); string example2 = getDocumentationExampleById("documentation/Concepts/interpretation.xml", "Alt_LateFnIntr_1"); xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare(move(example)); man->analyse(); if(!man->isPassRegistered(PassId::InterpretationPass)) { InterpretationPass* pass = new InterpretationPass(man); pass->run(); } int (* main)(int) = (int (*)(int)) man->run(); int result = main(10); ASSERT_EQ(21, result); XreateManager* man2 = XreateManager::prepare(move(example2)); ASSERT_TRUE(true); } //TOTEST call indirect recursion(w/o tags) //TASk implement and test Loop Inf (fix acc types in coco grammar) diff --git a/cpp/tests/latex.cpp b/cpp/tests/latex.cpp index 7fea3ef..3db5c5e 100644 --- a/cpp/tests/latex.cpp +++ b/cpp/tests/latex.cpp @@ -1,358 +1,358 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * Author: pgess * Created on June 25, 2018, 5:42 PM * * \file latex.cpp * \brief Testing of latex */ #include "xreatemanager.h" #include "pass/compilepass.h" #include "transcendlayer.h" #include "query/latex.h" -#include "compilation/latex.h" +#include "compilation/demand.h" #include "aux/xreatemanager-decorators.h" -#include "compilation/scopedecorators.h" +#include "compilation/decorators.h" #include "llvmlayer.h" #include "supplemental/docutils.h" -#include "supplemental/defines.h" +#include "supplemental/basics.h" #include #include using namespace xreate::latex; using namespace xreate::latereasoning; using namespace xreate::compilation; using namespace xreate; using namespace std; TEST(Latex, Script_NestedScopePropagation_1) { std::string program = R"CODE( import raw("scripts/cfa/context.lp"). fn = function:: int; entry { context:: test1. if(1==11)::int {2} else {3} } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* blockTrue = man->root->findFunction("fn")->getEntryScope()->getBody().blocks.front(); auto blockTrueP = man->transcend->pack(blockTrue); boost::format formatAlias("alias(%1%, %2%)."); man->transcend->addRawScript((formatAlias % blockTrueP % "block1").str()); man->transcend->addRawScript( R"SCRIPT( success1:- bind_scope(Block1, test1, strong); alias(Block1, block1). )SCRIPT"); man->analyse(); ASSERT_EQ(1, man->transcend->query("success1").size()); } TEST(Latex, Script_DemAndDecision_1) { std::string program = R"CODE( import raw("scripts/cfa/context.lp"). a = function:: int { context:: forC(a). c() } b = function:: int { context:: forC(b). c() } c = function:: int {0} main = function:: int; entry { a() + b() } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* blockC = man->root->findFunction("c")->getEntryScope(); auto blockCP = man->transcend->pack(blockC); boost::format formatAlias("alias(%1%, %2%)."); man->transcend->addRawScript((formatAlias % blockCP % "blockC").str()); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- alias(BlockC, blockC). latex_registered_subjects(forC, Variant):- bind_scope(_, Variant, strong); Variant = forC(_). )SCRIPT"); man->analyse(); ASSERT_EQ(1, man->transcend->query("latex_fn_demand").size()); ASSERT_EQ(2, man->transcend->query("latex_decision").size()); } TEST(Latex, LatexQuery_getFnDemand_1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). main = function:: int; entry { context:: alias(blockMain). 0 } )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- bind_scope(BlockC, alias(blockMain), strong). latex_registered_subjects(forC, decisionSome). )SCRIPT"); LatexQuery* query = new LatexQuery(); man->transcend->registerQuery(query, QueryId::LatexQuery); man->analyse(); Demand demand = query->getFnDemand("main"); ASSERT_EQ(1, demand.size()); ASSERT_STREQ("forC", demand.front().c_str()); } TEST(Latex, LatexQuery_getDecision_static_1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). a = function:: int {context::decisionSome. c()} b = function:: int {c()} c = function:: int {context:: alias(blockC). 0} )CODE"; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); man->transcend->addRawScript( R"SCRIPT( latex_scope_demand(BlockC, forC):- bind_scope(BlockC, alias(blockC), strong). latex_registered_subjects(forC, decisionSome). )SCRIPT"); LatexQuery* query = new LatexQuery(); man->transcend->registerQuery(query, QueryId::LatexQuery); man->analyse(); - LateAnnotation decisionLA = query->getDecision("forC", man->root->findFunction("a")->getEntryScope()); + LateAnnotation decisionLA = query->getValue("forC", man->root->findFunction("a")->getEntryScope()); auto decisionGS = decisionLA.select({}, man->root, man->transcend); ASSERT_TRUE(decisionGS); decisionGS->print(cout); cout << endl; auto decisionTuple = man->transcend->parse(*decisionGS); string decision = get<2>(decisionTuple); ASSERT_STREQ("decisionSome", decision.c_str()); } TEST(Latex, Compilation_1) { std::string program = R"CODE( a = function:: int {0} main = function:: int; entry { a() } )CODE"; string script = R"SCRIPT( latex_fn_demand(%1%, subject1). latex_decision(%2%, subject1, 5). latex_registered_subjects(subject1, 1). latex_registered_subjects(subject1, 5). )SCRIPT"; - typedef LatexBruteFunctionDecorator FnImpl; + typedef ExtraArgsBruteFnDecorator FnImpl; typedef LatexBruteScopeDecorator> ScopeImpl; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope()); boost::format format(script); man->transcend->addRawScript((format % "a" % scopeMainP).str()); man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); man->analyse(); std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); compiler->run(); man->llvm->initJit(); int(*fnMain)() = (int(*)())man->llvm->getFunctionPointer(compiler->getEntryFunction()); ASSERT_EQ(0, fnMain()); } // //TEST(Latex, Full1) { // std::string program = // R"CODE( // import raw("scripts/cfa/context.lp"). // // a = function:: int // { // context:: forC(a). // c() // } // // b = function:: int // { // context:: forC(b). // c() // } // // c = function:: int {0} // // main = function:: int; entry // { // a() + b() // } // )CODE"; // // string script = // R"SCRIPT( // alias(%1%, scopeC). // latex_scope_demand(ScopeC, forC) :- alias(ScopeC, scopeC). // latex_registered_subjects(forC, forC(a)). // latex_registered_subjects(forC, forC(b)). // )SCRIPT"; // -// typedef LatexBruteFunctionDecorator FnImpl; +// typedef DemandBruteFnDecorator FnImpl; // typedef LatexBruteScopeDecorator> ScopeImpl; // // std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); // ScopePacked scopeMainP = man->transcend->pack(man->root->findFunction("main")->getEntryScope()); // auto scopeCP = man->transcend->pack(man->root->findFunction("c")->getEntryScope()); // boost::format format(script); // man->transcend->addRawScript((format %scopeCP).str()); // man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); // man->analyse(); // // std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); // compiler->run(); // man->llvm->print(); //} // TEST(Latex, Compilation_TransitFn1){ std::string program = R"CODE( import raw("scripts/cfa/context.lp"). branchA = function:: int { context:: sink_a. fnTransit() } branchB = function:: int { context:: sink_b. fnTransit() } fnSink = function:: int {0} fnTransit = function:: int {fnSink()} main = function:: int; entry { branchA() + branchB() } )CODE"; string script = R"SCRIPT( alias(scopeSink, %1%). latex_scope_demand(ScopeSink, sink):- alias(scopeSink, ScopeSink). latex_registered_subjects(sink, sink_a). latex_registered_subjects(sink, sink_b). )SCRIPT"; - typedef LatexBruteFunctionDecorator FnImpl; + typedef ExtraArgsBruteFnDecorator FnImpl; typedef LatexBruteScopeDecorator> ScopeImpl; std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); CodeScope* scopeSink = man->root->findFunction("fnSink")->getEntryScope(); auto scopeSinkP = man->transcend->pack(scopeSink); ScopedSymbol argLatexSS{1, -1}; Symbol argLatexS{argLatexSS, scopeSink}; man->transcend->pack(argLatexS); boost::format format(script); man->transcend->addRawScript((format %scopeSinkP).str()); man->transcend->registerQuery(new LatexQuery(), QueryId::LatexQuery); man->analyse(); std::unique_ptr compiler(new compilation::CompilePassCustomDecorators(man.get())); compiler->run(); man->llvm->print(); man->llvm->initJit(); int(*fnMain)() = (int(*)()) man->llvm->getFunctionPointer(compiler->getEntryFunction()); int valueActual = fnMain(); ASSERT_EQ(0, valueActual); } TEST(Latex, Doc_Examples1){ std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Examples_1"); std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); } TEST(Latex, Doc_Examples2){ std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Examples_2"); std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); } TEST(Latex, Doc_ContextPropagation1){ std::string program =getDocumentationExampleById("documentation/Concepts/context.xml", "ContextPropagation1"); std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); } TEST(Latex, Doc_ContextPropagation2){ std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "ContextPropagation2"); std::unique_ptr man(details::tier1::XreateManager::prepare(move(program))); } TEST(Latex, Doc_Latex1){ std::string program = getDocumentationExampleById("documentation/Concepts/context.xml", "Latex1"); string script = R"SCRIPT( latef("compute"). latex_scope_demand(Scope, f_guarded(F)):- cfa_call(Scope, F); latef(F). latex_registered_subjects(f_guarded(F), Guard):- cfa_function_specializations(F, Guard); latef(F). late(TargetS, LatexParam, Guard, dfa_callguard(TargetS, Guard)):- dfa_callfn(TargetS, FnGuarded); latef(FnGuarded); latex_symbol(FnCallerBody, f_guarded(FnGuarded), LatexParam); TargetS=s(_,_, TargetScope); scope_fnbody(TargetScope, FnCallerBody); cfa_function_specializations(FnGuarded, Guard). )SCRIPT"; auto man(XreateManager::prepare(move(program))); man->transcend->addRawScript(move(script)); Fn3args fn = (Fn3args) man->run(); int resultActual = fn(1, 4, 3); ASSERT_EQ(7, resultActual); resultActual = fn(0, 4, 3); ASSERT_EQ(1, resultActual); } \ No newline at end of file diff --git a/cpp/tests/main.cpp b/cpp/tests/main.cpp index 2df18c6..8905f18 100644 --- a/cpp/tests/main.cpp +++ b/cpp/tests/main.cpp @@ -1,24 +1,25 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * main.cpp * * Created on: 00:00 Xreate Epoch * Author: pgess */ #include "utils.h" +#include "supplemental/basics.h" #include using namespace std; using namespace xreate; int main(int argc, char **argv) { testing::GTEST_FLAG(color) = "yes"; - string testsTemplate = Config::get("tests.template"); - string testsFilter = Config::get(string("tests.templates.") + testsTemplate); + string testsTemplate = Config::get("template"); + string testsFilter = Config::get(string("templates.") + testsTemplate); testing::GTEST_FLAG(filter) = testsFilter; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/cpp/tests/polymorph.cpp b/cpp/tests/polymorph.cpp index 5a930bc..bc35f39 100644 --- a/cpp/tests/polymorph.cpp +++ b/cpp/tests/polymorph.cpp @@ -1,160 +1,266 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * polymorph.cpp * * Author: pgess * Created on October 11, 2017, 8:37 PM */ #include "xreatemanager.h" #include "ast.h" #include "transcendlayer.h" #include "aux/latereasoning.h" #include #include "gtest/gtest.h" #include "query/polymorph.h" #include "supplemental/docutils.h" +#include "supplemental/basics.h" using namespace xreate; using namespace xreate::latereasoning; using namespace xreate::polymorph; using namespace std; TEST(Polymorphs, ast1) { - xreate::XreateManager* man = xreate::XreateManager::prepare( - R"CODE( - guard:: a { - test = function:: int {0} - } + xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE( + global-ann = type predicate { + entry + } - guard:: b { - test = function:: int {1} - } + context-ann = type predicate { + a, b + } - main = function:: int; entry { test() } + guard(::a) { + test = function:: int {0} + } -)CODE"); + guard(::b) { + test = function:: int {1} + } - const std::list& specs = man->root->getFunctionSpecializations("test"); - ASSERT_EQ(2, specs.size()); + main = function:: int; entry() { test() } + )CODE"); - auto itSpecs = specs.begin(); - ASSERT_EQ("a", (*itSpecs)->guard.getValueString()); - itSpecs++; - ASSERT_EQ("b", (*itSpecs)->guard.getValueString()); + const std::list& specs = man->root->getFnSpecializations("test"); + ASSERT_EQ(2, specs.size()); + + auto itSpecs = specs.begin(); + ASSERT_EQ("a", (*itSpecs)->guard.getValueString()); + itSpecs++; + ASSERT_EQ("b", (*itSpecs)->guard.getValueString()); } -TEST(Polymorphs, PolymorphQuery_Static_1) { - xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( - R"CODE( - import raw("scripts/dfa/polymorphism.lp"). +TEST(Polymorphs, Compile1){ + xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE( + global-ann = type predicate { + entry, guarded(value::int) + } - guard:: a { - test = function:: int {0} - } + context-ann = type predicate { + first(value::int), second + } - guard:: b { - test = function:: int {1} - } + guard(data::first) { + test = function:: int {data["value"]} + } - main = function:: int; entry { test()::int; callguard(b); dfa_polym(ret)} + guard(::second) { + test = function:: int {1} + } -)CODE"); + main = function:: int; entry() + { + x = 8:: int. + test()::int; guarded(x) + } + )CODE"); - man->analyse(); - PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); - - const Expression& bodyE = man->root->findFunction("main")->getEntryScope()->getBody(); - LateAnnotation decisionLA = query->get(bodyE); - ASSERT_EQ(1, decisionLA.guardedContent.size()); - - auto decisionOptSymb = decisionLA.select({}, man->root, man->transcend); - ASSERT_TRUE(decisionOptSymb); - - decisionOptSymb->print(cout); - cout << endl; - string guard = query->getValue(*decisionOptSymb).getValueString(); - ASSERT_STREQ("b", guard.c_str()); + man->transcend->addRawScript(R"(func_supply_guard(Site, first(Data), "context-ann"):- bind(Site, guarded(Data)).)"); + man->run(); } -TEST(Polymorphs, PolymorphQuery_Late_1){ - xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( -R"CODE( - Late = type variant{a, b}. - main = function:: int; entry{key= a():: Late; test. key::int} -)CODE"); +TEST(Polymorphs, Compile2){ + xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE( + guard-pred = type predicate { + first(value::int), second + } - man->transcend->addRawScript( -R"RULE( - late(S, S, a, dfa_callguard(S, a)):- - bind(S, test). - - late(S, S, b, dfa_callguard(S, b)):- - bind(S, test). -)RULE"); - man->analyse(); - PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); - - CodeScope* scopeMain = man->root->findFunction("main")->getEntryScope(); - Symbol keyS = Symbol{scopeMain->getSymbol("key"), scopeMain}; - - Expression keyE = scopeMain->getDefinition(keyS); - latereasoning::LateAnnotation answerLA = query->get(keyE); - Expression valueB(Operator::VARIANT, {}); valueB.setValueDouble(1); - auto answerRaw = answerLA.select({valueB}, man->root, man->transcend); - ASSERT_TRUE(answerRaw); - Expression answerE = query->getValue(*answerRaw); - ASSERT_STREQ("b", answerE.getValueString().c_str()); -} + global-ann = type predicate { + entry, guarded(guard-pred) + } + + guard(data ::first) { + test = function:: int {data["value"]} + } + + guard(::second) { + test = function:: int {1} + } -TEST(Polymorphs, PSCU_1){ - auto man = details::tier1::XreateManager::prepare(R"Code( - Dom = type variant {guard1, guard2}. - - guard:: guard1 { - compute = function :: int - {0} - } - - guard:: guard2 { - compute = function :: int - {1} - } - - test = function:: int; entry - { - xLate = guard2():: Dom. - y1= switch late (xLate:: Dom; alias(xLate)):: int - { - compute():: int; guardkey(xLate) - }. - y1 - } - )Code"); - - man->transcend->addRawScript(R"RAW( - dom(guard1; guard2). - late(Target, Key, Variant, dfa_callguard(Target, Variant)):- - bind(Target, guardkey(Alias)); - bind(Key, alias(Alias)); - dom(Variant). - )RAW"); - man->analyse(); - int (*program)() = (int (*)())man->run(); - int result = program(); - - ASSERT_EQ(1, result); + main = function:: int; entry() + { + x = first(8):: guard-pred. + test()::int; guarded(x) + } + )CODE"); + + man->transcend->addRawScript(R"(func_supply_guard(Site, Guard, "guard-pred"):- bind(Site, guarded(Guard)).)"); + man->run(); } -TEST(Polymorphs, Doc_FnLvlPoly_1){ - string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "FnLvlPoly_1"); - auto man = XreateManager::prepare(move(example)); - ASSERT_TRUE(true); +TEST(Polymorphs, CompileException1){ + + xreate::XreateManager* man = xreate::XreateManager::prepare(R"CODE( + Global-ann = type predicate { + entry + } + + HandlerDivByZero = type { + ret:: int + } + + Guards = type predicate { + fast, + safe(handlerZeroDiv:: HandlerDivByZero) + } + + guard(:: fast) { + div = function(a::int, b::int) { a / b} + } + + guard(handlers:: safe) { + div = function(a::int, b::int)::int + { + if (b != 0):: int + {a / b} else { handlers["handlerZeroDiv", "ret"] } + } + } + + main = function:: int; entry() + { + handler = { ret = 11 } ::HandlerDivByZero. + div(8, 0):: int; safe(handler) + } +)CODE"); + + man->transcend->addRawScript(R"(func_supply_guard(Site, Guard, "Guards"):- bind(Site, Guard).)"); + FnNoArgs fn = (FnNoArgs) man->run(); + int result = fn(); + ASSERT_EQ(11, result); } -TEST(Polymorphs, Doc_LatePoly_1){ - string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "LatePoly_1"); - auto man = XreateManager::prepare(move(example)); - ASSERT_TRUE(true); -} \ No newline at end of file +//TEST(Polymorphs, PolymorphQuery_Static_1) { +// xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( +// R"CODE( +// import raw("scripts/dfa/polymorphism.lp"). +// +// guard:: a { +// test = function:: int {0} +// } +// +// guard:: b { +// test = function:: int {1} +// } +// +// main = function:: int; entry { test()::int; callguard(b); dfa_polym(ret)} +// +//)CODE"); +// +// man->analyse(); +// PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); +// +// const Expression& bodyE = man->root->findFunction("main")->getEntryScope()->getBody(); +// LateAnnotation decisionLA = query->get(bodyE); +// ASSERT_EQ(1, decisionLA.guardedContent.size()); +// +// auto decisionOptSymb = decisionLA.select({}, man->root, man->transcend); +// ASSERT_TRUE(decisionOptSymb); +// +// decisionOptSymb->print(cout); +// cout << endl; +// string guard = query->getValue(*decisionOptSymb).getValueString(); +// ASSERT_STREQ("b", guard.c_str()); +//} +// +//TEST(Polymorphs, PolymorphQuery_Late_1){ +// xreate::details::tier1::XreateManager* man = xreate::details::tier1::XreateManager::prepare( +//R"CODE( +// Late = type variant{a, b}. +// main = function:: int; entry{key= a():: Late; test. key::int} +//)CODE"); +// +// man->transcend->addRawScript( +//R"RULE( +// late(S, S, a, dfa_callguard(S, a)):- +// bind(S, test). +// +// late(S, S, b, dfa_callguard(S, b)):- +// bind(S, test). +//)RULE"); +// man->analyse(); +// PolymorphQuery* query = dynamic_cast (man->transcend->getQuery(QueryId::PolymorphQuery)); +// +// CodeScope* scopeMain = man->root->findFunction("main")->getEntryScope(); +// Symbol keyS = Symbol{scopeMain->getSymbol("key"), scopeMain}; +// +// Expression keyE = scopeMain->getDefinition(keyS); +// latereasoning::LateAnnotation answerLA = query->get(keyE); +// Expression valueB(Operator::VARIANT, {}); valueB.setValueDouble(1); +// auto answerRaw = answerLA.select({valueB}, man->root, man->transcend); +// ASSERT_TRUE(answerRaw); +// Expression answerE = query->getValue(*answerRaw); +// ASSERT_STREQ("b", answerE.getValueString().c_str()); +//} +// +//TEST(Polymorphs, PSCU_1){ +// auto man = details::tier1::XreateManager::prepare(R"Code( +// Dom = type variant {guard1, guard2}. +// +// guard:: guard1 { +// compute = function :: int +// {0} +// } +// +// guard:: guard2 { +// compute = function :: int +// {1} +// } +// +// test = function:: int; entry +// { +// xLate = guard2():: Dom. +// y1= switch late (xLate:: Dom; alias(xLate)):: int +// { +// compute():: int; guardkey(xLate) +// }. +// y1 +// } +// )Code"); +// +// man->transcend->addRawScript(R"RAW( +// dom(guard1; guard2). +// late(Target, Key, Variant, dfa_callguard(Target, Variant)):- +// bind(Target, guardkey(Alias)); +// bind(Key, alias(Alias)); +// dom(Variant). +// )RAW"); +// man->analyse(); +// int (*program)() = (int (*)())man->run(); +// int result = program(); +// +// ASSERT_EQ(1, result); +//} +// +//TEST(Polymorphs, Doc_FnLvlPoly_1){ +// string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "FnLvlPoly_1"); +// auto man = XreateManager::prepare(move(example)); +// ASSERT_TRUE(true); +//} +// +//TEST(Polymorphs, Doc_LatePoly_1){ +// string example = getDocumentationExampleById("documentation/Concepts/polymorphism.xml", "LatePoly_1"); +// auto man = XreateManager::prepare(move(example)); +// ASSERT_TRUE(true); +//} \ No newline at end of file diff --git a/cpp/tests/polymorphism-dt.cpp b/cpp/tests/polymorphism-dt.cpp new file mode 100644 index 0000000..b0765f6 --- /dev/null +++ b/cpp/tests/polymorphism-dt.cpp @@ -0,0 +1,48 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * polymorphism-dt.cpp + * + * Author: pgess + * Created on 26/02/2020 + */ + +#include "xreatemanager.h" +#include "ast.h" +#include "pass/compilepass.h" +#include "compilation/targetinterpretation.h" +#include "gtest/gtest.h" +#include "llvmlayer.h" + +using namespace xreate; +using namespace xreate::interpretation; +using namespace std; + +TEST(PDT, BuildDTTable1){ + Attachments::init(); + FILE* code = fopen("scripts/dfa/build-table.xreate", "r"); + assert(code != nullptr); + auto man = xreate::details::tier2::XreateManager::prepare(code); + CompilePass* compiler = new compilation::CompilePassCustomDecorators<>(man); + TargetInterpretation* targetI12n = new interpretation::TargetInterpretation(man, compiler); + ManagedFnPtr fn = man->root->findFunction("buildTable"); + + I12nFunctionSpec fnI12nReport; + fnI12nReport.flagPartialInterpretation = true; + fnI12nReport.signature.push_back(INTR_ONLY); + fnI12nReport.signature.push_back(INTR_ONLY); + Attachments::put(fn, fnI12nReport); + + PIFnSignature fnSignature; + fnSignature.declaration = fn; + fnSignature.bindings.push_back(Expression()); + fnSignature.bindings.push_back(Expression(Atom(1))); + + PIFunction* fnI12n = targetI12n->getFunction(move(fnSignature)); + llvm::Function* fnRaw = fnI12n->compile(); + + man->llvm->print(); + +// man-> +// PIFunction +} diff --git a/cpp/tests/problems.cpp b/cpp/tests/problems.cpp new file mode 100644 index 0000000..df96bbd --- /dev/null +++ b/cpp/tests/problems.cpp @@ -0,0 +1,112 @@ +// +// Created by pgess on 26/03/2020. +// + +#include "xreatemanager.h" +#include "pass/compilepass.h" +#include "llvmlayer.h" +#include "supplemental/basics.h" + +#include "gtest/gtest.h" + +using namespace xreate; + +TEST(Problems, MinMax1){ + struct Pair {intmax_t x; intmax_t y;}; + typedef Pair (*FnPair)(); + + FILE* code = fopen("scripts/containers/minmax.xreate", "r"); + assert(code != nullptr); + + auto man = details::tier1::XreateManager::prepare(code); + LLVMLayer* llvm = man->llvm; + man->analyse(); + + std::unique_ptr compiler(new compilation::CompilePassCustomDecorators<>(man)); + compiler->prepare(); + llvm::Function* fnMinMax1Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax1"))->compile(); + llvm::Function* fnMinMax2Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax2"))->compile(); + llvm::Function* fnMinMax3Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax3"))->compile(); + llvm::Function* fnMinMax4Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax4"))->compile(); + llvm::Function* fnMinMax5Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax5"))->compile(); + llvm::Function* fnMinMax6Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax6"))->compile(); + llvm::Function* fnMinMax7Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax7"))->compile(); + llvm::Function* fnMinMax8Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax8"))->compile(); + llvm::Function* fnMinMax9Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax9"))->compile(); + llvm::Function* fnMinMax10Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax10"))->compile(); + llvm::Function* fnMinMax11Raw = compiler->getFunctionUnit(man->root->findFunction("fn-minmax11"))->compile(); + llvm->print(); + + llvm->initJit(); + { + FnPair fnMinxMax1 = (FnPair) llvm->getFunctionPointer(fnMinMax1Raw); + Pair resultMinMax1 = fnMinxMax1(); + ASSERT_EQ(3, resultMinMax1.x); + ASSERT_EQ(37, resultMinMax1.y); + } + + { + FnNoArgs fnMinMax2 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax2Raw); + intmax_t resultMinMax2 = fnMinMax2(); + ASSERT_EQ(10, resultMinMax2); + } + + { + FnNoArgs fnMinMax3 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax3Raw); + intmax_t resultMinMax3 = fnMinMax3(); + ASSERT_EQ(146, resultMinMax3); + } + + { + FnNoArgs fnMinMax4 = (FnNoArgs) llvm->getFunctionPointer(fnMinMax4Raw); + intmax_t resultMinMax4 = fnMinMax4(); + ASSERT_EQ(6, resultMinMax4); + } + + { + FnPair fnMinMax5 = (FnPair) llvm->getFunctionPointer(fnMinMax5Raw); + Pair resultMinMax5 = fnMinMax5(); + ASSERT_EQ(4, resultMinMax5.x); + ASSERT_EQ(11, resultMinMax5.y); + } + + { + FnPair fnMinMax6 = (FnPair) llvm->getFunctionPointer(fnMinMax6Raw); + Pair resultMinMax6 = fnMinMax6(); + ASSERT_EQ(2, resultMinMax6.x); + ASSERT_EQ(8, resultMinMax6.y); + } + + { + FnPair fnMinMax7 = (FnPair) llvm->getFunctionPointer(fnMinMax7Raw); + Pair resultMinMax7 = fnMinMax7(); + ASSERT_EQ(1, resultMinMax7.x); + ASSERT_EQ(3, resultMinMax7.y); + } + + { + FnPair fnMinMax8 = (FnPair) llvm->getFunctionPointer(fnMinMax8Raw); + Pair resultMinMax8 = fnMinMax8(); + ASSERT_EQ(2, resultMinMax8.x); + ASSERT_EQ(3, resultMinMax8.y); + } + + { + FnPair fnMinMax9 = (FnPair) llvm->getFunctionPointer(fnMinMax9Raw); + Pair resultMinMax9 = fnMinMax9(); + ASSERT_EQ(1, resultMinMax9.x); + ASSERT_EQ(8, resultMinMax9.y); + } + + { + FnNoArgs fnMinMax10 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax10Raw); + intmax_t resultMinMax10 = fnMinMax10(); + ASSERT_EQ(1, resultMinMax10); + } + + { + FnNoArgs fnMinMax11 = (FnNoArgs ) llvm->getFunctionPointer(fnMinMax11Raw); + intmax_t resultMinMax11 = fnMinMax11(); + ASSERT_EQ(8, resultMinMax11); + } +} \ No newline at end of file diff --git a/cpp/tests/supplemental/basics.cpp b/cpp/tests/supplemental/basics.cpp new file mode 100644 index 0000000..67bf827 --- /dev/null +++ b/cpp/tests/supplemental/basics.cpp @@ -0,0 +1,11 @@ +// +// Created by pgess on 12/03/2020. +// + +#include "basics.h" + +Config Config::__self = Config(); + +Config::Config() +: __storage{json_file{ "config/default.json" }} +{} \ No newline at end of file diff --git a/cpp/tests/supplemental/basics.h b/cpp/tests/supplemental/basics.h new file mode 100644 index 0000000..98562b0 --- /dev/null +++ b/cpp/tests/supplemental/basics.h @@ -0,0 +1,37 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * + * File: defines.h + * Author: pgess + * + * Created on May 7, 2019, 8:11 PM + */ + +#include "jeayeson/jeayeson.hpp" +#include + +#ifndef DEFINES_H +#define DEFINES_H + + + +typedef intmax_t (*FnNoArgs)(); +typedef intmax_t (*Fn1Args)(intmax_t); +typedef intmax_t (*Fn2Args)(intmax_t, intmax_t); +typedef intmax_t (*Fn3Args)(intmax_t, intmax_t, intmax_t); + +class Config { +private: + json_map __storage; + static Config __self; + Config(); + +public: + static std::string get(std::string key) { + return __self.__storage.get_for_path(key).get(); + } +}; + +#endif /* DEFINES_H */ + diff --git a/cpp/tests/supplemental/defines.h b/cpp/tests/supplemental/defines.h deleted file mode 100644 index f51b989..0000000 --- a/cpp/tests/supplemental/defines.h +++ /dev/null @@ -1,21 +0,0 @@ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - * - * - * File: defines.h - * Author: pgess - * - * Created on May 7, 2019, 8:11 PM - */ - -#ifndef DEFINES_H -#define DEFINES_H - -typedef int (*FnNoArgs)(); -typedef int (*Fn1Args)(int); -typedef int (*Fn2args)(int, int); -typedef int (*Fn3args)(int, int, int); - - -#endif /* DEFINES_H */ - diff --git a/cpp/tests/transcend.cpp b/cpp/tests/transcend.cpp index e7a53ac..e5d6436 100644 --- a/cpp/tests/transcend.cpp +++ b/cpp/tests/transcend.cpp @@ -1,93 +1,102 @@ /* 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)). + test3(1, "a"). )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()); + + typedef std::tuple Predicate3; + solution = man->transcend->query("test3"); + Gringo::Symbol symbTest3 = solution.begin()->second; + auto answer3a = get<0>(man->transcend->parse(schemeT(), symbTest3)); + auto answer3b = get<1>(man->transcend->parse(schemeT(), symbTest3)); + ASSERT_EQ(1, answer3a); + ASSERT_STREQ("a", answer3b.c_str()); } 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); } 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(); std::cout << outputActual << std::endl; string outputExpected = "warning(\"Visibility violation\",\"test\",\"sum\")"; ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); } \ No newline at end of file diff --git a/cpp/tests/types.cpp b/cpp/tests/types.cpp index 110d315..5592981 100644 --- a/cpp/tests/types.cpp +++ b/cpp/tests/types.cpp @@ -1,248 +1,311 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * types.cpp * * Created on: Jun 4, 2015 * Author: pgess */ #include "gtest/gtest.h" #include "xreatemanager.h" #include "llvmlayer.h" #include "main/Parser.h" #include "transcendlayer.h" #include "analysis/typeinference.h" -#include "analysis/interpretation.h" +#include "analysis/utils.h" using namespace std; using namespace xreate; using namespace xreate::grammar::main; TEST(Types, DependantTypes1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string], \n" " content:: string\n" "}.\n"; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeXmlNode = program->root->findType("XmlNode"); - ASSERT_EQ(TypeOperator::LIST_RECORD, typeXmlNode->__operator); + ASSERT_EQ(TypeOperator::RECORD, typeXmlNode->__operator); ASSERT_EQ(3, typeXmlNode->__operands.size()); ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(0).__value); - ASSERT_EQ(TypeOperator::LIST_ARRAY, typeXmlNode->__operands.at(1).__operator); + ASSERT_EQ(TypeOperator::ARRAY, typeXmlNode->__operands.at(1).__operator); ASSERT_EQ(TypePrimitive::String, typeXmlNode->__operands.at(2).__value); } TEST(Types, ast_ParameterizedTypes_FeatureTypeIndex_1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string],\n" " content:: string\n" "}.\n" "" "Template = type(Leaf) {Leaf, [Leaf[\"content\"]]}." "Concrete = type Template(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); - ASSERT_EQ(TypeOperator::LIST_RECORD, typeConcrete->__operator); + ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operator); ASSERT_EQ(2, typeConcrete->__operands.size()); - ASSERT_EQ(TypeOperator::LIST_RECORD, typeConcrete->__operands.at(0).__operator); + ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operands.at(0).__operator); - ASSERT_EQ(TypeOperator::LIST_ARRAY, typeConcrete->__operands.at(1).__operator); + ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator); ASSERT_EQ(TypePrimitive::String, typeConcrete->__operands.at(1).__operands.at(0).__value); } TEST(Types, TreeType1) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " attrs:: [string],\n" " content:: string\n" "}.\n" "" "Tree = type(Leaf) {Leaf, [Tree(Leaf)]}." "Concrete = type Tree(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); - ASSERT_EQ(TypeOperator::LIST_RECORD, typeConcrete->__operator); + ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operator); ASSERT_EQ(2, typeConcrete->__operands.size()); - ASSERT_EQ(TypeOperator::LIST_RECORD, typeConcrete->__operands.at(0).__operator); + ASSERT_EQ(TypeOperator::RECORD, typeConcrete->__operands.at(0).__operator); - ASSERT_EQ(TypeOperator::LIST_ARRAY, typeConcrete->__operands.at(1).__operator); + ASSERT_EQ(TypeOperator::ARRAY, typeConcrete->__operands.at(1).__operator); auto typeLink = typeConcrete->__operands.at(1).__operands.at(0); - ASSERT_EQ(TypeOperator::LINK, typeLink.__operator); - ASSERT_EQ(typeConcrete->conjuctionId, typeLink.conjuctionId); +// ASSERT_EQ(TypeOperator::LINK, typeLink.__operator); +// ASSERT_EQ(typeConcrete->linkId, typeLink.linkId); } TEST(Types, TreeType1LLvm) { string&& code = "XmlNode = type {\n" " tag:: string,\n" " /* attrs:: [string],*/\n" " content:: string\n" "}.\n" "" "Tree = type(Leaf) {Leaf, [Tree(Leaf)]}." "Concrete = type Tree(XmlNode)."; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeConcrete = program->root->findType("Concrete"); llvm::Type* raw = program->llvm->toLLVMType(typeConcrete); } TEST(Types, ArrayOfExternal1) { FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("childrenRaw"))); - EXPECT_EQ(t2->__operator, TypeOperator::LIST_ARRAY); + EXPECT_EQ(t2->__operator, TypeOperator::ARRAY); } TEST(Types, ExternType1) { FILE* input = fopen("scripts/containers/Containers_Implementation_LinkedList1.xreate", "r"); assert(input != nullptr); Scanner scanner(input); Parser parser(&scanner); parser.Parse(); AST* ast = parser.root->finalize(); CodeScope* body = ast->findFunction("test")->getEntryScope(); const ExpandedType& t2 = ast->getType(body->getDefinition(body->getSymbol("tree"))); - EXPECT_EQ(t2->__operator, TypeOperator::CUSTOM); + EXPECT_EQ(t2->__operator, TypeOperator::ALIAS); } TEST(Types, ast_VariantType1) { string&& code = " colors = type variant {RED, BLUE, GREEN}.\n" " test = function:: colors; entry {GREEN()}"; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typ = program->root->findType("colors"); EXPECT_EQ(TypeOperator::VARIANT, typ->__operator); Expression eRed = program->root->findFunction("test")->getEntryScope()->getBody(); EXPECT_EQ(Operator::VARIANT, eRed.op); const ExpandedType& typ2 = program->root->getType(eRed); EXPECT_EQ(TypeOperator::VARIANT, typ2->__operator); } TEST(Types, full_VariantType_Switch1) { string&& code = "colors = type variant{RED, BLUE, GREEN}. \n" " test = function:: colors {GREEN()} \n" "main = function:: int; entry { \n" " switch(test()):: int \n" " case (GREEN()) {0} \n" " case default {1} \n" "}"; XreateManager* man = XreateManager::prepare(move(code)); int (*main)() = (int (*)()) man->run(); EXPECT_EQ(0, main()); } TEST(Types, ast_VariantType2) { std::string script = R"Code( Annotation = type variant { - Num:: int, - String:: string, - Func:: {name::string, arguments::[Expression]} + Num(int), + String(string), + Func(name::string, arguments::[Expression]) }. )Code"; std::unique_ptr program(XreateManager::prepare(move(script))); ExpandedType typ = program->root->findType("Annotation"); ASSERT_EQ(3, typ.get().fields.size()); } TEST(Types, SlaveTypes_UnwrapSlaveType1) { auto man = details::tier1::XreateManager::prepare(R"Code( AtomNumT = type slave atomNumT. AtomStrT = type slave atomStrT. CmpndIntStrT = type slave cmpndIntStrT. VariantT = type slave variantT. VariantComplicatedT = type slave variantComplicatedT. )Code"); man->transcend->addRawScript(R"RAW( atomNumT(5). atomNumT(8). atomStrT("a"). atomStrT("b"). cmpndIntStrT(1, "a"). cmpndIntStrT(2, "b"). variantT(first). variantT(second). variantT(third). variantComplicatedT(first(1, "a")). variantComplicatedT(second("b")). )RAW"); man->analyse(); ExpandedType AtomNumT = man->root->findType("AtomNumT"); ASSERT_EQ(AtomNumT->__operator, TypeOperator::SLAVE); - ExpandedType ContentAtomNumT = interpretation::dereferenceSlaveType(AtomNumT, man->transcend); - ASSERT_EQ(TypePrimitive::Num, ContentAtomNumT->__value); + ExpandedType ContentAtomNumT = analysis::dereferenceSlaveType(AtomNumT, man->transcend); + ASSERT_EQ(TypePrimitive::Int, ContentAtomNumT->__value); ExpandedType AtomStrT = man->root->findType("AtomStrT"); - ExpandedType ContentAtomStrT = interpretation::dereferenceSlaveType(AtomStrT, + ExpandedType ContentAtomStrT = analysis::dereferenceSlaveType(AtomStrT, man->transcend); ASSERT_EQ(TypePrimitive::String, ContentAtomStrT->__value); ExpandedType CmpndIntStrT = man->root->findType("CmpndIntStrT"); - ExpandedType ContentCmpndIntStrT = interpretation::dereferenceSlaveType(CmpndIntStrT, + ExpandedType ContentCmpndIntStrT = analysis::dereferenceSlaveType(CmpndIntStrT, man->transcend); - ASSERT_EQ(TypeOperator::LIST_RECORD, ContentCmpndIntStrT->__operator); + ASSERT_EQ(TypeOperator::RECORD, ContentCmpndIntStrT->__operator); ASSERT_EQ(2, ContentCmpndIntStrT->__operands.size()); ExpandedType VariantT = man->root->findType("VariantT"); - ExpandedType ContentVariantT = interpretation::dereferenceSlaveType(VariantT, + ExpandedType ContentVariantT = analysis::dereferenceSlaveType(VariantT, man->transcend); ASSERT_EQ(TypeOperator::VARIANT, ContentVariantT->__operator); ASSERT_EQ(3, ContentVariantT->fields.size()); ExpandedType VariantComplicatedT = man->root->findType("VariantComplicatedT"); - ExpandedType ContentVariantComplicatedT = interpretation::dereferenceSlaveType(VariantComplicatedT, + ExpandedType ContentVariantComplicatedT = analysis::dereferenceSlaveType(VariantComplicatedT, man->transcend); ASSERT_EQ(TypeOperator::VARIANT, ContentVariantComplicatedT->__operator); ASSERT_EQ(2, ContentVariantComplicatedT->fields.size()); ASSERT_EQ(2, ContentVariantComplicatedT->__operands.at(0).__operands.size()); } TEST(Types, IndexNumber_1) { string&& code = R"CODE( Tuple = type {string, int}. Int = type Tuple[1]. )CODE"; std::unique_ptr program(XreateManager::prepare(move(code))); ExpandedType typeInt = program->root->findType("Int"); } + +TEST(Types, RecursiveTypes_1){ +string&& code = +R"CODE( + Node = type { + data:: int, + next:: ref(Node) + } +)CODE"; + std::string outputExpected = "{ i32, %Node* }"; + + std::unique_ptr program(XreateManager::prepare(move(code))); + ExpandedType typNode = program->root->findType("Node"); + llvm::Type* typNodeRaw = program->llvm->toLLVMType(typNode); + + std::string outputActual; + llvm::raw_string_ostream os(outputActual); + typNodeRaw->print(os); + os.flush(); + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Types, RecursiveTypes_2){ + string&& code = + R"CODE( + Tree = type(NodeData) variant { + LEAF(data:: NodeData), + GROUP(nodes:: [ref(Tree(NodeData))]) + } + + Guard = type variant { + a, b + }. + + DecisionTree = type Tree(Guard). +)CODE"; + + std::string outputExpected = "{ i8, { { i8 } } "; + + std::unique_ptr program(XreateManager::prepare(move(code))); + ExpandedType typNode = program->root->findType("DecisionTree"); + llvm::Type* typNodeRaw = program->llvm->toLLVMType(typNode); + + std::string outputActual; + llvm::raw_string_ostream os(outputActual); + typNodeRaw->print(os); + os.flush(); + cout << outputActual << endl; + ASSERT_NE(std::string::npos, outputActual.find(outputExpected)); +} + +TEST(Types, NestedList1){ + string code = R"( +test = function:: int; entry() +{ + x = {a = {b = 3}}:: {a:: {b:: int}}. + x["a", "b"] +} +)"; + + std::unique_ptr program(XreateManager::prepare(move(code))); + program->run(); +} \ No newline at end of file diff --git a/documentation-api/diagrams/interpretation.graphml b/documentation-api/diagrams/interpretation.graphml index 4d43541..3460b34 100644 --- a/documentation-api/diagrams/interpretation.graphml +++ b/documentation-api/diagrams/interpretation.graphml @@ -1,502 +1,520 @@ - + - + - I12n - Pass - Compilation/Interpretation + I12n + Pass + Compilation/Interpretation - + - + - + - Function -Interpretation -Data + I12n +Function +Spec - + - Function + Function Interpretation Helper - + - I12n + I12n Data - + - InterpretationOperator + InterpretationOperator - + - InterpretationPass + InterpretationPass - + - I12n + I12n Query - + - I12n + I12n Resolution - + - IPass + IPass - + - I12n + I12n Function - + - I12n + I12n Scope - + - I12n + I12n Scope Decorator - + - Intrinsic + Intrinsic Query Instruction - + - PIF -Signature + PIFnSignature - + - PI -Function + PIFunction - + - PI -FunctionUnit + PIBruteFunction - + - TargetInterpretation + TargetInterpretation - + - ICodeScope + ICodeScope - + - Target + Target - + - IFunctionUnit + IBruteFunction - Unfinished + Unfinished - - - - + + - + - + - + - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/documentation-api/diagrams/latex.graphml b/documentation-api/diagrams/latex.graphml index c537821..a7306de 100644 --- a/documentation-api/diagrams/latex.graphml +++ b/documentation-api/diagrams/latex.graphml @@ -1,819 +1,819 @@ - + - Latex - Compilation - Transcend - Interpretation + Latex + Compilation + Transcend + Interpretation - + - ExtraArgs + ExtraArgs FnInvocation - Latex + Latex BruteScope Dec - ICodeScopeUnit + ICodeScopeUnit - Latex + Latex BruteFunction Dec - IFunctionUnit + IFunctionUnit - getSubjectDomain + getSubjectDomain - + - Latex + Latex Query - + getFnDemand - IQuery + IQuery - collapseColumn + collapseColumn - Latex + Latex Query - represent + represent Trans Expression - + - Latex script + Latex script - Latex script + Latex script - Latex - Output - Processing - Input + Latex + Output + Processing + Input - Demand + Demand upstream - latex_scope_demand + latex_scope_demand - latex_registered_subjects + latex_registered_subjects - scope_demand(1) + scope_demand(1) - fn_demand + fn_demand - decision + decision - scope_demand(2) + scope_demand(2) - late(decision) + late(decision) - scope_fn + scope_fn - fn_demand_ordered + fn_demand_ordered - latex_symbol + latex_symbol diff --git a/documentation-api/diagrams/polymorphism.graphml b/documentation-api/diagrams/polymorphism.graphml index baa6ea4..38c38fe 100644 --- a/documentation-api/diagrams/polymorphism.graphml +++ b/documentation-api/diagrams/polymorphism.graphml @@ -1,338 +1,332 @@ - + Polymorphism Compilation Transcend - + Polymorph Query - + IQuery - + Polymorph CodeScopeUnit findFunction - + ICodeScopeUnit - + Polymorph FnInvocation P.Query - + PSCU - + IFnInvocation InterpretationScope LateReasoningCompiler - - - + - - - + - + + - - - - + diff --git a/documentation/Concepts/interpretation.xml b/documentation/Concepts/interpretation.xml index e5e6ec3..5a160ac 100644 --- a/documentation/Concepts/interpretation.xml +++ b/documentation/Concepts/interpretation.xml @@ -1,882 +1,882 @@ <?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?>Queries 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 }
<?xxe-sn 2fxwo1r89a8 3?>Querying Example: GUI 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 the 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 a platform's 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 + bottom_of(ref :: Block), + left_of(ref :: 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)} + {drawVerical(blockA, blockB["ref"])} case (left_of) - {drawHorizontal(blockA, blockB)} + {drawHorizontal(blockA, blockB["ref"])} } 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 9d47543..560e26b 100644 --- a/documentation/Syntax/syntax.xml +++ b/documentation/Syntax/syntax.xml @@ -1,1290 +1,1290 @@ <?xxe-sn 26yv439af40 5a?>Syntax There is a number of principles the Xreate syntax is based on: Follows SSA(single assignment) form: each identifier is defined only once and no redefinitions allowed. 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 Xreate expressions have a form: SYNTAX: //expression// [:: //type//; //annotations-list// ] annotation-list is a list of annotations delimited by semicolon. Expressions consist of literals and various operations as follows: Literals numbers, strings: 5, "Nimefurahi kukujua". Lists, records Record is a collection of elements of different types - {year = 1934, 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". List and record operations 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 Functions 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 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//. ].. } 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; entry { a + b:: int a = 10:: int. b = 2:: int. } 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. A computation order depends only on dependencies between expressions. This approach has properties as follows: Mutually independent identifiers can be evaluated in any order. 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-block// function-name Name of a function. argument Formal parameters. Arguments are delimited by comma. type, return-type Formal parameter's and returning value's types. function-block Code block that acts as a function's definition. annotations List of annotations delimited by semicolon. 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 }
<?xxe-sn 26yv439af40 8j?>Function Specializations SYNTAX: **guard::** //annotation// { //functions-list// } annotation Guard expressed in the form of an annotation. functions-list One or more functions that share the same guard. 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 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. if (-isLastOpOverflow(result)):: int { result } else { overflowErrorCode() } } } guard:: fast_enviroment { sum = function (a::int, b::int) :: int { 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 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// ].. //block-true// **else** //block-false// The if statement executes block-true or block-false depending 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].. **case default** //default-code-block// condition Expression to to decide which branch to execute next. guard Value to match against condition. default-code-block Executed if no appropriate case found. The switch statement evaluation's result is that of the branch whose guard matches the condition. An example: name="tests/ast.cpp: AST.Doc_BranchStatements" monthName = switch(monthNum) :: string case (1) {"Jan"} case (2) {"Feb"} case default {"It's strange..an unexpected month"}.
<?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// init-value Initial value a loop starts from. accumulator Identifier which holds loop's result after each iteration. For each iteration accumulator 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. 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" //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). 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" //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 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-body// list Container to iterate over. element Identifier that assumes value of a currently processing list element. type, annotations Expression types and optional annotations delimited by semicolon. init-value Accumulator's initial value loop starts from. accumulator Identifier that assumes loop-body evaluation result after each iteration. 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 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 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-body// list Container to iterate over. element Identifier that assumes value of a currently processed list element. type, annotations Type and optional annotations delimited by semicolon. 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] { 2 * number }.
<?xxe-sn 26yv439af40 c6?>Types Primitive Types: bool Booleans. i8, i32, i64 Signed integers; 8, 32, 64 bit wide respectively. int, num 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. float Double precision floating-point numbers. string 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. * An unspecified type. Postpones type checks for this expression. Examples: x = {amount=200, currency="USD"}::*. Compound types: element-type ] List of elements of the same type element-type. Examples: x = {1, 2, 3}:: [int] - list of int's. Lists can have different internal implementations. { key:: type, ... } Record: a list of elements of different types possibly with named keys. Examples: {int, string}, {name::string, age::int}. variant {option :: {type, ...}, ...} 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 Denotes a type constructed by Transcend. See slave types. An example: slave unit_test. Type operations: type [ key ] Index operation: accessing elements of a compound type. Examples: Bio = type {birth_year:: int, name:: string}. YearBirth = type Bio[birth_year]. type ( parameters... ) Constructs a concrete type with the given parameters. Examples: MyTree = type Tree(int). New types are defined as follows: SYNTAX: //type-name// = **type** (//parameters//...) //type-definition// . Examples: name="tests/ast.cpp: AST.Doc_Types" Tuple = type {string, int}. 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} + 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?>SWITCH VARIANT Statement SYNTAX: **switch variant** ( //condition// [**->** //alias// ] [:: //type// [; //annotations//... ] ] ) :: type [; annotations... ] [ **case** ( //guard// ) //case-branch// ]... condition Expression of a variant type. alias Identifier to denote unwrapped content of the condition expression withing case branches. guard Name of a variant to match against actual condition's variant. case-branch Code block to execute in case of the matched variant. The condition expression's content is referred to by alias within the branch. 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. An example: name="tests/ast.cpp: AST.Doc_VariantsSwitch1",lines=15 Month = type variant { - MonByName :: {name:: string}, - MonById :: {id:: 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 2fds09lgw74 n?>Versions //identifier// [**{** //version// **}**] version Number to specify the identifier's version. Versions is a language construct to deal with mutable data. An example: name="tests/ast.cpp: AST.Doc_Versions1" Date = type {year:: int, month:: string}. test = function:: Date; entry { x{0} = {year = 1953, month = "March"}:: Date. x{1} = x{0} + {month = "February"}:: Date. //updates a single record's field x{1} //returned value } In the example above x{0}, x{1} are different versions of the same variable. This is a simple trick with the idea that for all intents and purposes x{0}, x{1} behave like different variables but really aren't. All analyses treat them as different immutable variables, yet the compiler actually uses the same memory address for them making this an update of a variable. It is a hint from a developer that the only one version of a variable should be available at any given time. The only analysis that knows the truth is the versions analysis. It is responsible for code validation in order to make sure that there is no expression that uses an outdated/unknown/unreachable variable's version. An another (counter)example: COUNTEREXAMPLE, name="tests/ast.cpp: AST.Doc_Versions1" x{0} = 8:: int. x{1} = x{0} + 10:: int. y = x{0} + x{1} :: int. //invalid code: uses several versions The versions analysis builds a variables' liveliness graph to track versions usage and reveal situations when it's impossible to compute an expression due to the fact that it refers to an (possibly) outdated or unreachable version.
<?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:: string; entry { employee = getAnyEmployee():: Employee. primaryKey = "surname":: string. employee[primaryKey] } 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: name="tests/ast.cpp: AST.Doc_RecUpdate1" Day = type { year:: int, month:: string, day:: int }. test = function:: Day { tomorrow today = {year = 1936, month = "July", day = 21}:: Day. tomorrow = today + {day = 22}:: Day. }
\ No newline at end of file +?> diff --git a/documentation/index.xml b/documentation/index.xml index 6bd404c..e29172d 100644 --- a/documentation/index.xml +++ b/documentation/index.xml @@ -1,434 +1,434 @@ <?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" refers to the developer oriented side meaning exactly an ability to easily write, read, reuse, 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. See an example. 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: logger = createFileLogger("/some/file"):: Logger. ... write(logger, "First log entry"). ... write(logger, "Last log entry"). Exploitation reasoning allows to determine when it is the first, last access to resources such as files, in other words, it infers access order. As a result it is possible to automatically initialize / destruct related resources. Unlike RAII, an another related technique, Exploitation is reserved to manage resources usage that spans across different parts of a program: modules, plugins, etc. Virtualization reasoning also helps to work with external resources by enabling 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:: int, b:: int):: int { a / b } } guard:: iAmVerySafe { div = function(a:: int, b:: int):: int { if ( b == 0 ):: int { zeroDivisionErrCode() } else { a / b } } } test = function:: int; 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 the 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 decisions to guide various aspects of compilation process, particularly by selecting appropriate specializations as in the above example.
<?xxe-sn 2fchdmt7vgg 2?>More Advanced Example Suppose we write a program to generate 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 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 a 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 not only memory overhead but delays response latency(time before the first block is sent) 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 + errcode(data::int), + message(data::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.
<?xxe-sn 2fs1xxghz93 -wunr7fl0rw8t?>Differences from other languages it is convenient to talk about intentions in order to outline a vast landscape of programming languages and point out the place of Xreate on it, i.e. to compare languages depending on do they allow to express a developer's intentions along with raw code that can be used on many occasions, e.g. to validate the code's correctness. Traditionally type system is used to declare intentions. At closer look, types in (imperative) statically typed languages contain inseparable combination of the following: Intentions, such as "that variable is a string". Usage patterns: a new code should play nicely with the rest of a program, e.g. if a program works with unicode, a new string variable should be also of unicode family type(even if this is not necessary for a given part of code) Platform constraints, e.g. if a platform supports wide strings natively, a new string variable should also be a wide string. In this regard, in general, statically typed languages are overconstrained, they require developers to provide more information than they intend to. This usually hinders code reuse and adaptation; to work on a new platform, software requires porting: a process of re-expressing underlying intentions once again with the new platform's constraints. On the other side, dynamically typed languages are underconstrained, since they do not allow to express all desirable intentions. This leads to disastrous inefficiency and code fragility because any errors can be caught only at runtime. As an example, OOP languages are hugely successful among other reasons because they provide classes to more finely express intentions, e.g. String, UnicodeString, Utf8String with exact behaviour being hidden in implementation details. Xreate in its turn is based on the idea to allow developers express as much or as little intentions as they want to. This is a way to reach one of the the language's goals to facilitate writing of easily reusable and adaptable software. On a syntax level, annotations are used to express intentions, e.g. string;i_dont_need(utf8)(note the modality, annotations can express boundaries, preferences, etc. ). This approach obviously leads to problems with software's performance and efficiency. For most high level languages, compilers rely on defaults to fill in unspecified by the developer gaps in the exact implementation details. Usually languages provide standard, default data structures the developer have no control over. However Xreate follows another approach: to resolve this contradiction, the compiler determines necessary implementation details not only depending on annotations, but also by looking at usage patterns along with platform properties trying to infer most efficient implementations in any given circumstances.
diff --git a/grammar/xreate.ATG b/grammar/xreate.ATG index 84ac92c..42ffdbd 100644 --- a/grammar/xreate.ATG +++ b/grammar/xreate.ATG @@ -1,707 +1,828 @@ //TODO add ListLiteral //TODO ExprTyped: assign default(none) type #include "ast.h" #include "ExternLayer.h" #include #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 +details::inconsistent::AST* root = nullptr; // current program unit - void ensureInitalizedAST(){ - if (root == nullptr) root = new details::inconsistent::AST(); - } +void SemErr(std::initializer_list msgs){ + std::wstringstream output; + for(const auto& msg: msgs){output << msg;} + SemErr(output.str().c_str()); +} + +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 checkListIndex() +{ + return la->kind == _lcurbrack && 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); + return token2 == _assign && token3 == _function; } 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); - } +void recognizeIdentifier(Expression& id, const std::wstring& hint){ + if (!context.scope) + SemErr({L"Identifier found in undefined scope: ", hint}); + + if (!context.scope->recognizeIdentifier(id)){ + root->postponeIdentifier(context.scope, id); + } } enum SwitchKind{SWITCH_NORMAL, SWITCH_META}; CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". any = ANY - '"'. digit = "0123456789". cr = '\r'. lf = '\n'. tab = '\t'. TOKENS ident = (letter ['-' letter] | '_') {letter ['-' letter] | digit | '_' }. number = 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 | IF(checkFuncDecl()) FDecl (. root->add(function); .) | TDecl | SkipModulesSection )} (. .) . -Ident -= ident (. name = t->val; .). +Ident= + ident (. name = t->val; .). + +// recognition +IdentR = (. std::wstring name; .) + Ident (. e = Expression(Atom(name)); .) + (. recognizeIdentifier(e, name); .). + +//versioning +IdentV= (. std::wstring name; .) + Ident (. e = Expression(Atom(name)); .) + [ Version ]. -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 ] +//recognition + versioning +IdentVR= (. std::wstring name; .) + Ident (. e = Expression(Atom(name)); .) + [ Version ] (. recognizeIdentifier(e, name); .) . + +Version= + lcurbrack ( + ident (. SemErr({L"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; .) +FDecl = (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; Expression binding; .) Ident assign -[pre (. flagIsPrefunct = true; .)] -function (. f = new Function(fname); f->isPrefunction = flagIsPrefunct; CodeScope* entry = f->getEntryScope(); .) +function (. f = new Function(fname); CodeScope* entry = f->getEntryScope(); .) -[lparen Ident tagcolon ExprAnnotations (. f->addBinding(Atom(argName), move(binding)); .) +[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 }] +[ tagcolon 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. +GuardSection<>= (. std::wstring arg, guardI; Expression guardE, guardBinding; Function* f; TypeAnnotation guardT; .) + "guard" lparen + [Ident] tagcolon Ident (. guardE = Expression(Operator::CALL, {Atom(guardI)}); bool res = root->recognizeVariantConstructor(guardE); .) + (. if(!res) SemErr(coco_string_create("Can't recognize a guard"));.) + (. if (!arg.empty()) guardE.addBindings({Atom(arg)}); .) + (. guardBinding.type = TypeAnnotation(TypeOperator::GUARD, {guardE.type}); guardBinding.type.__valueCustom = Atom(guardI).get(); .) + rparen lcurbrack { + FDecl (. f->guard = guardE; if (!arg.empty()){f->addBinding(Atom(arg), Expression(guardBinding));} .) + (. 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; .) + ( "string" (. typ = TypePrimitive::String;.) + | "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 +| TPred | TSlave +| TRef | 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); .) +| Ident (. typ = TypeAnnotation(TypeOperator::ALIAS, {}); typ.__valueCustom = Atom(tid).get(); .) + [lparen Type (. 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}); .) + lbrack Type rbrack (. typ = TypeAnnotation(TypeOperator::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. +TRecordBody = + (. TypeAnnotation t; std::wstring key; size_t keyCounter=0; + typ = TypeAnnotation(TypeOperator::RECORD, {}); + .) +{ + ( + IF(checkTokenAfterIdent(_tagcolon)) Ident tagcolon + | (. key = to_wstring(keyCounter++); .) + ) + Type [comma] (. typ.__operands.push_back(t); .) + (. typ.fields.push_back(Atom(key).get()); .) +}. + +TRecord = + lcurbrack TRecordBody rcurbrack + (. if(!typ.__operands.size()) SemErr(coco_string_create("Record type can't be empty.")); .) +. + +TVariantRec = (. TypeAnnotation typVoid; .) + lparen TRecordBody rparen + (. if(typ.__operands.size()==0) typ = typVoid; .) +. -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(); .) +TVariantBody = (. TypeAnnotation t, typVoid; std::vector operands; std::vector> keys; std::wstring v; .) +lcurbrack + { (. t = typVoid; .) + Ident [TVariantRec] (. keys.push_back(Atom(v)); operands.push_back(t); .) + [comma] + } +rcurbrack + (. typ = TypeAnnotation(TypeOperator::VARIANT, {}); + typ.__operands = operands; + typ.addFields(std::move(keys)); + .) +. + +TVariant= + "variant" TVariantBody + (. if(!typ.__operands.size()) SemErr(coco_string_create("Variant type can't be empty.")); .) +. + +TPred= + "predicate" TVariantBody + (. if(!typ.__operands.size()) SemErr(coco_string_create("Predicate type can't be empty.")); .) +. + +TSlave= + "slave" (. typ = TypeAnnotation(TypeOperator::SLAVE, {}); .) + lparen + string (. typ.__valueCustom = Atom(t->val).get(); .) + rparen +. + +TRef= (. TypeAnnotation typChild; .) + "ref" lparen Type rparen (. typ = TypeAnnotation(TypeOperator::REF, {typChild}); .) . 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)); .) + {comma Ident (. args.push_back(Atom(arg)); .) } rparen] - Typeperiod (. t.addBindings(move(args)); root->add(move(t), Atom(tname)); .) + Type[period] (. t.addBindings(move(args)); root->add(move(t), Atom(tname)); .) . ContextDecl = (. Expression tag; .) -context tagcolon +"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); +VDecl = (. Expression var, value;.) + IdentV assign ExprTyped (. Symbol identSymbol = f->addDefinition(move(var), move(value)); + Attachments::put(value, identSymbol); .) . -BDecl = lcurbrack (. Expression body; pushContextScope(scope); .) +BDecl = lcurbrack (. Expression body; pushContextScope(scope); bool flagBodyFound = false; .) {(IF(checkAssignment()) VDecl period // | RuleContextDecl | ContextDeclperiod - | ExprTyped (. scope->setBody(body); Attachments::put(body, Symbol{ScopedSymbol::RetSymbol, scope});.) + | ExprTyped (. scope->setBody(body); flagBodyFound = true; Attachments::put(body, Symbol{ScopedSymbol::RetSymbol, scope});.) )} - rcurbrack (. popContextScope(); .) + rcurbrack (. if(!flagBodyFound) SemErr(coco_string_create("Code block with an empty body!")); + 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 - (. + (. + Expression varAccBindingE; varAccBindingE.type = e.type; block->addBinding(Atom(varEl), move(tagsEl)); - block->addBinding(Atom(varAcc), Expression()); + block->addBinding(Atom(varAcc), move(varAccBindingE)); .) BDecl<&*block> (. e.addBlock(block); .) | lparen Expr implic Ident rparen (. - e = Expression(Operator::INF, {eAcc}); + e = Expression(Operator::FOLD_INF, {eAcc}); e.addBindings({Atom(varAcc)}); - block->addBinding(Atom(varAcc), Expression()); .) - tagcolon ExprAnnotations BDecl<&*block> + tagcolon ExprAnnotations + (. + Expression varAccBindingE; varAccBindingE.type = e.type; + block->addBinding(Atom(varAcc), move(varAccBindingE)); + .) + 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 + Ident< name> + (. outer = Expression(Operator::CALL_INTRINSIC, {}); + outer.setValue(Atom(name)); + root->recognizeIntrinsic(outer); + .) + 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 +// | "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)); .) -. +// InterfaceExternC<> = (. ExternData data; .) +// lcurbrack {ExternHeadersDecl | ExternAliasDecl } rcurbrack +// (. root->addExternData(move(data)); .) +// . +// +// ExternPkgDecl = +// "pkgconfig" lparen +// string (. package = t->val.) +// rparen +// . +// +// ExternAliasDecl = (. std::wstring alias, package; .) +// Ident assign "library" lparen ExternPkgDecl rparen period +// (. data.addLibAlias(Atom(alias), Atom(package)); .) +// . +// +// ExternHeadersDecl = (. std::list listInc; std::wstring& package; .) +// "include" +// [lparen +// ( +// Ident (. data.requireLibAlias(Atom(alias)); .) +// | ExternPkgDecl (. data.requireLibPackage(Atom(package)); .) +// ) +// rparen] +// lcurbrack { string (. listInc.push_back(Atom(t->val).get()); .) +// [comma] } rcurbrack [period] (. data.requireHeaders(listInc); .) +// . 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 . /* - 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 // ). 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)}); .) -). + ( + '-' MetaSimpExpr (. e = Expression(Operator::NEG, {e2}); .) + | IF(checkParametersList()) Ident + (. e = Expression(Operator::CALL, {Expression(Atom(i1))}); + if (!root->recognizeVariantConstructor(e)) + SemErr({L"Undefined predicate: ", 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)); + .) + | IdentR + | number (. e = Expression(Atom(t->val)); .) + ). 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))); .) // . // 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; .) +Expr< Expression& e> (. Expression e2; .) += ExprLogicAnd + [ ('or' | 'OR') Expr (. e = Expression(Operator::OR, {e, e2}); .) + ] +. + +ExprLogicAnd< Expression& e> (. Expression e2; .) += ExprRel + [ ('and' | 'AND') ExprLogicAnd (. e = Expression(Operator::AND, {e, e2}); .) + ] +. + +ExprRel< Expression& e> (. Operator op; Expression e2; .) = ExprArithmAdd - [ RelOp - ExprArithmAdd (. e = Expression(op, {e, e2}); .) - ]. + [ RelOp ExprRel (. e = Expression(op, {e, e2}); .) + ] +. ExprArithmAdd< Expression& e>= (. Operator op; Expression e2; .) ExprArithmMul< e> - [ AddOp< op> - ExprArithmAdd< e2> (. e = Expression(op, {e, e2});.) + [ + AddOpExprArithmAdd< e2> (. e = Expression(op, {e, e2});.) ]. ExprArithmMul< Expression& e> (. Operator op; Expression e2; .) -= ExprPostfix< e> += ExprUpdate [ MulOp< op> ExprArithmMul< e2> (. e = Expression(op, {e, e2}); .) ]. + +ExprUpdate= (. Expression e2; .) + ExprPostfix< e> + [ + colon + ( IF(checkListIndex()) ListIndexLiteral + | ListLiteral) (. e = Expression(Operator::UPDATE, {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 + | IdentVR + | 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; .) +ListLiteral = (. std::wstring key; + Expression val; + std::list> keys; + e = Expression(Operator::LIST, {}); + .) + lcurbrack { + ( IF(checkTokenAfterIdent(_assign)) Ident assign + | (. key = L""; .) + ) Expr (. keys.push_back(Atom(key)); + e.operands.push_back(val); + .) + [comma] } rcurbrack (. e.addBindings(keys.begin(), keys.end()); .) +. + +ListIndexLiteral = (. e = Expression(Operator::LIST_INDEX, {});Expression valE;.) 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()); .) - . + { (. Expression idxE(Operator::LIST, {});.) + lbrack CalleeParams rbrack + assign Expr[comma] (. e.operands.push_back(idxE); e.operands.push_back(valE); .) + } + rcurbrack +. 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/containers/RecUpdateInLoop1.xreate b/scripts/containers/RecUpdateInLoop1.xreate new file mode 100644 index 0000000..18f677e --- /dev/null +++ b/scripts/containers/RecUpdateInLoop1.xreate @@ -0,0 +1,12 @@ +Rec = type {x:: int, y:: int}. + +test = function(base:: int):: int; entry() { + fields = intrinsic rec_fields("Rec"):: [string]; i12n(on()). + + result = loop fold(fields->field:: string, {undef, 0}->ctx):: {rec:: Rec, id:: int} + { + { (ctx["rec"]::Rec) : {[field] = base + ctx["id"] }, ctx["id"]+1 }:: {rec:: Rec, id:: int} + }. + + result["rec"]["y"] +} diff --git a/scripts/containers/minmax.xreate b/scripts/containers/minmax.xreate new file mode 100644 index 0000000..9b9b8c6 --- /dev/null +++ b/scripts/containers/minmax.xreate @@ -0,0 +1,241 @@ +El = type {idx:: int, val:: int}. +PairT = type(X) {X, X}. + +Pair = type PairT(int). +PairEl = type PairT(El). + +min = function(x:: int, y:: int):: int +{ + if (x < y):: int { x } else { y } +} + +max = function(x:: int, y:: int):: int +{ + if (x > y):: int { x } else { y } +} + +minEl = function(e1:: El, e2:: El):: El +{ + if (e1["val"] < e2["val"]):: El {e1} else {e2} +} + +maxEl = function(e1:: El, e2:: El):: El +{ + if (e1["val"] > e2["val"]):: El {e1} else {e2} +} + +minElIdx = function(x:: El, y:: El):: El +{ + if (x["idx"] < y["idx"]):: El {x} else {y} +} + +maxElIdx = function(x:: El, y:: El):: El +{ + if (x["idx"] > y["idx"]):: El {x} else {y} +} + +minLstEl = function(x:: El, y:: El):: El +{ + if (x["val"] <= y["val"]):: El {x} else {y} +} + +maxLstEl = function(x:: El, y:: El):: El +{ + if (x["val"] >= y["val"]):: El {x} else {y} +} + +fn-minmax1 = function:: {min:: int, max:: int} +{ + arr = {25, 37, 12, 6, 5, 19, 3, 20}:: [int]. + + loop fold(arr->el:: int, {1000, 0}->state):: {min:: int, max:: int} + { + { + min(el, state["min"]), + max(el, state["max"]) + }:: {min:: int, max:: int} + } +} + +fn-minmax2 = function:: int +{ + arr = { + {1, 72}, {3, 8}, {2, 5}, {3, 4}, {11, 1}, {6, 5}, {9, 2} + } :: [{int, int}]. + + loop fold(arr->rect:: {int, int}, 1000->squareMin):: int + { + square = rect[0] * rect[1]:: int. + + min(square, squareMin) + } +} + +fn-minmax3 = function:: int +{ + arr = { + {1, 72}, {3, 8}, {2, 5}, {3, 4}, {11, 1}, {6, 5}, {9, 2} + } :: [{int, int}]. + + loop fold(arr->rect:: {int, int}, 0->perMax):: int + { + per = 2 * (rect[0] + rect[1]):: int. + max(per, perMax) + } +} + +fn-minmax4 = function:: int +{ + arr = {25, 37, 12, 6, 5, 19, 3, 20}:: [int]. + minInit = {0, 25}:: El. + result = loop fold(arr->el:: int, {0, minInit}->state):: {idx:: int, minEl:: El} + { + elCur = {state["idx"], el}:: El. + idxNext = state["idx"] + 1:: int. + + {idxNext, minEl(elCur, state["minEl"])}:: {idx:: int, minEl:: El} + }. + + result["minEl", "idx"] +} + +fn-minmax5 = function:: El +{ + input = { + {1, 72}, {3, 8}, {2, 5}, {3, 4}, {11, 1}, {6, 5}, {9, 2} + } :: [{int, int}]. + + resultInit = {0, 0}:: El. + result = loop fold(input->el:: Pair, {0, resultInit}->acc):: {idx:: int, max:: El} + { + density = el[0] / el[1] :: int. + idxNext = acc["idx"] + 1:: int. + elCur = {acc["idx"], density}:: El. + + {idxNext, maxEl(elCur, acc["max"])}:: {idx:: int, max:: El} + }. + + result["max"] +} + +fn-minmax6 = function:: Pair +{ + arr = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + + init = {0, {{1000, 1000}, {0, 0}}}:: {int, PairEl}. + + result = loop fold(arr->val:: int, init->acc):: {int, PairEl} + { + el = {acc[0], val}:: El. + + idx = acc[0]+1 :: int. + min = minEl(el, acc[1, 0]) :: El. + max = maxLstEl(el, acc[1, 1]):: El. + + {idx, ({min, max}:: PairEl)}:: {int, PairEl} + }. + + {result[1][0]["idx"], result[1][1]["idx"]} +} + +fn-minmax7 = function:: Pair +{ + arr = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + + init = {0, {{0, 0}, {1000, 1000}}}:: {int, PairEl}. + + result = loop fold(arr->val:: int, init->acc):: {int, PairEl} + { + el = {acc[0], val}:: El. + + idx = acc[0]+1 :: int. + max = maxEl(el, acc[1, 0]) :: El. + min = minLstEl(el, acc[1, 1]):: El. + + {idx, ({max, min}:: PairEl)}:: {int, PairEl} + }. + + {result[1][0]["idx"], result[1][1]["idx"]} +} + +fn-minmax8 = function:: Pair +{ + input = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + init = {0, {{1000, 1000}, {1000, 1000}}}:: {int, PairEl}. + + result = loop fold(input->val:: int, init->acc):: {int, PairEl} + { + el = {acc[0], val}:: El. + idx = acc[0]+1 :: int. + minF = minEl(el, acc[1, 0]) :: El. + minL = minLstEl(el, acc[1, 1]):: El. + + {idx, {minF, minL}}:: {int, PairEl} + }. + + {result[1, 0, "idx"], result[1, 1, "idx"]} +} + +fn-minmax9 = function:: Pair +{ + input = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + init = {0, {{0, 0}, {0, 0}}}:: {int, PairEl}. + + result = loop fold(input->val:: int, init->acc):: {int, PairEl} + { + el = {acc[0], val}:: El. + idx = acc[0]+1 :: int. + maxF = maxEl(el, acc[1, 0]) :: El. + maxL = maxLstEl(el, acc[1, 1]):: El. + + {idx, {maxF, maxL}}:: {int, PairEl} + }. + + {result[1, 0, "idx"], result[1, 1, "idx"]} +} + +fn-minmax10 = function:: int +{ + input = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + init = {0, {{1000, 1000}, {0, 0}}}:: {int, PairEl}. + + result = loop fold(input->val:: int, init->acc):: {int, PairEl} + { + el = {acc[0], val}:: El. + idx = acc[0]+1 :: int. + minF = minEl(el, acc[1, 0]) :: El. + maxF = maxEl(el, acc[1, 1]):: El. + + {idx, {minF, maxF}}:: {int, PairEl} + }. + + (minElIdx(result[1, 0], result[1, 1])::El)["idx"] +} + +fn-minmax11 = function:: int +{ + input = {25, 37, 1, 1, 6, 5, 19, 3, 37, 20}:: [int]. + + ctxInit = {0, {{1000, 1000}, {0, 0}}}:: {int, PairEl}. + result = loop fold(input-> val:: int, ctxInit->ctx):: {int, PairEl} + { + elCur = {ctx[0], val}:: El. + + elMinL = minLstEl(elCur, ctx[1, 0]):: El. + elMaxL = maxLstEl(elCur, ctx[1, 1]):: El. + + {ctx[0]+1, {elMinL, elMaxL}}:: {int, PairEl} + }. + + (maxElIdx(result[1, 0], result[1, 1])::El)["idx"] +} + +fn-minmax12 = function:: int +{ + input = {-8, 16, 3, -5, 2, -11, -905, -54}:: [int]. + + loop fold(input->val:: int, 0->minPos):: int + { + if (val > 0 { min(val, minPos) } else { minPos } + } +} diff --git a/scripts/dfa/build-table.xreate b/scripts/dfa/build-table.xreate new file mode 100644 index 0000000..90deb77 --- /dev/null +++ b/scripts/dfa/build-table.xreate @@ -0,0 +1,22 @@ +Tree = type(NodeData) variant { + LEAF(data:: NodeData), + GROUP(nodes:: [ref(Tree(NodeData))]) +} + +Guard = type variant { + a, b +}. + +DecisionTree = type Tree(Guard). + +buildTable = function(nodes:: [DecisionTree], size:: i32):: [ DecisionTree ] +{ + tableInit = arr_create(size):: [DecisionTree]. + + loop fold(nodes->node:: DecisionTree, tableInit->table):: [DecisionTree] + { + switch variant(node):: [DecisionTree] + case (LEAF) { table + { LEAF(node["data"]) } } + case (GROUP) { table + GROUP(buildTable(nodes["nodes"])) } + } +} diff --git a/scripts/execution-order/test-1.xreate b/scripts/execution-order/test-1.xreate index 239d1a8..2f4dee5 100644 --- a/scripts/execution-order/test-1.xreate +++ b/scripts/execution-order/test-1.xreate @@ -1,49 +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)} + case (errcode) {content["data"]} + case (message) {printf("%s", content["data"])} } 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) } }