pax_global_header00006660000000000000000000000064143665360200014517gustar00rootroot0000000000000052 comment=bfa41f087a92b72370893c9b36758487a18dc6a0 maeparser-1.3.1/000077500000000000000000000000001436653602000135005ustar00rootroot00000000000000maeparser-1.3.1/.azure-pipelines/000077500000000000000000000000001436653602000166725ustar00rootroot00000000000000maeparser-1.3.1/.azure-pipelines/linux_build.yml000066400000000000000000000026271436653602000217420ustar00rootroot00000000000000steps: - bash: | source ${CONDA}/etc/profile.d/conda.sh sudo chown -R ${USER} ${CONDA} conda config --set always_yes yes --set changeps1 no conda update -q conda conda info -a conda create --name maeparser_build $(compiler) cmake \ boost-cpp=$(boost_version) boost=$(boost_version) \ libboost=$(boost_version) zlib displayName: Setup build environment - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build mkdir build && cd build && \ cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DMAEPARSER_RIGOROUS_BUILD=ON \ -DBoost_NO_SYSTEM_PATHS=ON \ -DCMAKE_INCLUDE_PATH="${CONDA_PREFIX}/include" \ -DCMAKE_LIBRARY_PATH="${CONDA_PREFIX}/lib" \ -DMAEPARSER_BUILD_SHARED_LIBS=$(shared_lib) displayName: Configure build (Run CMake) - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build cd build make -j $( $(number_of_cores) ) displayName: Build - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build cd build ctest -j $( $(number_of_cores) ) --output-on-failure -T Test displayName: Run tests - task: PublishTestResults@2 inputs: testResultsFormat: "CTest" testResultsFiles: "build/Testing/*/Test.xml" testRunTitle: $(system.phasedisplayname) CTest Test Run maeparser-1.3.1/.azure-pipelines/mac_build.yml000066400000000000000000000047531436653602000213450ustar00rootroot00000000000000steps: - bash: | wget https://github.com/phracker/MacOSX-SDKs/releases/download/10.15/MacOSX$(target_platform).sdk.tar.xz tar Jxvf MacOSX$(target_platform).sdk.tar.xz displayName: Install MacOSX $(target_platform) SDK - script: | echo "Removing homebrew from Azure to avoid conflicts." curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/uninstall > ~/uninstall_homebrew chmod +x ~/uninstall_homebrew ~/uninstall_homebrew -fq rm ~/uninstall_homebrew displayName: Remove homebrew - bash: | source ${CONDA}/etc/profile.d/conda.sh sudo chown -R ${USER} ${CONDA} echo -e "backend: TkAgg\n" > $HOME/.matplotlib/matplotlibrc conda config --set always_yes yes --set changeps1 no conda update -q conda conda create --name maeparser_build $(compiler) cmake \ boost-cpp=$(boost_version) boost=$(boost_version) \ py-boost=$(boost_version) libboost=$(boost_version) displayName: Setup build environment - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build export SDKROOT="$(pwd)/MacOSX$(target_platform).sdk/" export CONDA_BUILD_SYSROOT=${SDKROOT} mkdir build && cd build && \ cmake .. \ -DCMAKE_BUILD_TYPE=Release \ -DMAEPARSER_RIGOROUS_BUILD=ON \ -DBoost_NO_SYSTEM_PATHS=ON \ -DCMAKE_OSX_SYSROOT=${SDKROOT} \ -DCMAKE_OSX_DEPLOYMENT_TARGET=$(target_platform) \ -DCMAKE_INCLUDE_PATH="${CONDA_PREFIX}/include" \ -DCMAKE_LIBRARY_PATH="${CONDA_PREFIX}/lib" \ -DMAEPARSER_BUILD_SHARED_LIBS=$(shared_lib) displayName: Configure build (Run CMake) - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build export SDKROOT="$(pwd)/MacOSX$(target_platform).sdk/" export CONDA_BUILD_SYSROOT=${SDKROOT} cd build make -j $( $(number_of_cores) ) displayName: Build - bash: | source ${CONDA}/etc/profile.d/conda.sh conda activate maeparser_build export DYLD_FALLBACK_LIBRARY_PATH=${CONDA_PREFIX}/lib:${DYLD_FALLBACK_LIBRARY_PATH} export SDKROOT="$(pwd)/MacOSX$(target_platform).sdk/" export CONDA_BUILD_SYSROOT=${SDKROOT} cd build ctest -j $( $(number_of_cores) ) --output-on-failure -T Test displayName: Run tests - task: PublishTestResults@2 inputs: testResultsFormat: "CTest" testResultsFiles: "build/Testing/*/Test.xml" testRunTitle: $(system.phasedisplayname) CTest Test Run maeparser-1.3.1/.azure-pipelines/vs_build.yml000066400000000000000000000026051436653602000212270ustar00rootroot00000000000000steps: - powershell: Write-Host "##vso[task.prependpath]$env:CONDA\Scripts" displayName: Setup build environment - script: | conda config --set always_yes yes --set changeps1 no conda info -a conda create --name maeparser_build $(compiler) cmake ^ boost-cpp=$(boost_version) boost=$(boost_version) ^ py-boost=$(boost_version) libboost=$(boost_version) displayName: Install dependencies - script: | set Boost_ROOT= mkdir build && cd build call activate maeparser_build cmake .. ^ -G "Visual Studio 16 2019" ^ -DCMAKE_BUILD_TYPE=Release ^ -DMAEPARSER_RIGOROUS_BUILD=ON ^ -DBoost_NO_SYSTEM_PATHS=ON ^ -DCMAKE_INCLUDE_PATH=%CONDA_PREFIX%/Library/include ^ -DCMAKE_LIBRARY_PATH=%CONDA_PREFIX%/Library/lib ^ -DMAEPARSER_BUILD_SHARED_LIBS=$(shared_lib) displayName: Configure build (Run CMake) - script: | call activate maeparser_build cd build cmake --build . --config Release -j $(number_of_cores) displayName: Build - script: | call activate maeparser_build cd build ctest -j $(number_of_cores) --output-on-failure -T Test -C Release displayName: Run tests - task: PublishTestResults@2 inputs: testResultsFormat: "CTest" testResultsFiles: "build/Testing/*/Test.xml" testRunTitle: $(system.phasedisplayname) CTest Test Run maeparser-1.3.1/.clang-format000066400000000000000000000035271436653602000160620ustar00rootroot00000000000000--- Language: Cpp AccessModifierOffset: -2 ConstructorInitializerIndentWidth: 4 AlignEscapedNewlinesLeft: false AlignTrailingComments: true AllowAllParametersOfDeclarationOnNextLine: true AllowShortBlocksOnASingleLine: false AllowShortCaseLabelsOnASingleLine: false AllowShortIfStatementsOnASingleLine: false AllowShortLoopsOnASingleLine: false AllowShortFunctionsOnASingleLine: Inline AlwaysBreakAfterDefinitionReturnType: false AlwaysBreakTemplateDeclarations: false AlwaysBreakBeforeMultilineStrings: false BreakBeforeBraces: Linux BreakBeforeBinaryOperators: None BreakBeforeTernaryOperators: true BreakConstructorInitializersBeforeComma: false BinPackParameters: true BinPackArguments: true ColumnLimit: 80 ConstructorInitializerAllOnOneLineOrOnePerLine: false DerivePointerAlignment: false ExperimentalAutoDetectBinPacking: false IndentCaseLabels: false IndentWrappedFunctionNames: false IndentFunctionDeclarationAfterType: false MaxEmptyLinesToKeep: 1 KeepEmptyLinesAtTheStartOfBlocks: true NamespaceIndentation: None ObjCSpaceAfterProperty: false ObjCSpaceBeforeProtocolList: true PenaltyBreakBeforeFirstCallParameter: 19 PenaltyBreakComment: 300 PenaltyBreakString: 1000 PenaltyBreakFirstLessLess: 120 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left SpacesBeforeTrailingComments: 1 Cpp11BracedListStyle: true Standard: Cpp11 IndentWidth: 4 TabWidth: 8 UseTab: Never SpacesInParentheses: false SpacesInSquareBrackets: false SpacesInAngles: false SpaceInEmptyParentheses: false SpacesInCStyleCastParentheses: false SpaceAfterCStyleCast: true SpacesInContainerLiterals: true SpaceBeforeAssignmentOperators: true ContinuationIndentWidth: 4 CommentPragmas: '^ IWYU pragma:' ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] SpaceBeforeParens: ControlStatements DisableFormat: false ... maeparser-1.3.1/.gitignore000066400000000000000000000003331436653602000154670ustar00rootroot00000000000000tags *.o # dependency files *.d # programs drop_cache # gcov files *.gch *.gcda *.gcno *.gcov # leftovers from clang analyze *.plist .*.swp .DS_Store *.dtps boost unittest env.sh test_write.mae* #Vim temporary files *~ maeparser-1.3.1/Buffer.cpp000066400000000000000000000133621436653602000154220ustar00rootroot00000000000000#include #include #include #include #include #include "Buffer.hpp" namespace schrodinger { Buffer::Buffer(size_t buffer_size) : m_data(buffer_size) { begin = current = m_data.begin(); // Set end to begin since no data has been loaded. end = begin; } Buffer::Buffer(std::istream& stream, size_t buffer_size) : Buffer(buffer_size) { m_loader = new StreamLoader(stream); } Buffer::Buffer(FILE* file, size_t buffer_size) : Buffer(buffer_size) { m_loader = new FileLoader(file); } Buffer::Buffer(const std::string& str) : Buffer(str.size()) { const char* c = str.c_str(); std::copy(c, c + str.size(), m_data.begin()); } Buffer::~Buffer() { if (m_loader != nullptr) { delete m_loader; } } size_t Buffer::getColumn(const char* ptr) const { assert(ptr >= begin && ptr <= end); const char* save = ptr; while (ptr > begin) { --ptr; if (*ptr == '\n') { return save - ptr; } }; return (save - ptr) + m_starting_column; } bool Buffer::load(char*& save) { // begin, current, end are public member variables if (current < end) { return true; } if (m_loader == nullptr) { return false; } size_t new_size = m_data.size(); if (new_size == 0) { new_size = m_loader->getDefaultSize(); } size_t saved_chars = 0; if (save != nullptr) { saved_chars = end - save; if (saved_chars > new_size / 2) { new_size = saved_chars * 2; } } BufferData data(new_size); if (m_loader->load(data, save, end)) { m_starting_column = this->getColumn(); // line_number stays the same m_data = data; begin = save = m_data.begin(); current = begin + saved_chars; end = begin + m_data.size(); return true; } else { // If nothing new got loaded, keep the Buffer unchanged and return // false return false; } } std::ostream& operator<<(std::ostream& os, const Buffer& b) { size_t max_length = 10; size_t head_length = std::min(max_length, b.size()); std::string head(b.begin, b.begin + head_length); os << "Buffer(" << head << "...)"; return os; } BufferData::BufferData(size_t size) : m_size(size) { // Allocate space and add a trailing null character. m_data.resize(m_size + 1); m_data[m_size] = '\0'; } void BufferData::resize(size_t size) { if (size >= m_data.size()) { throw std::runtime_error("BufferData size can't be increased."); } m_size = size; m_data[m_size + 1] = '\0'; } bool BufferDataCollector::load(BufferData& data, const char* begin, const char* end) const { bool succeeded = m_loader->load(data, begin, end); if (succeeded) { m_tokens_list->appendBufferData(data); } return succeeded; } size_t BufferDataCollector::readData(char*, size_t) const { // char* ptr, size_t size are unnamed to avoid compilation warning messages. // The BufferDataCollector doesn't actually read any data directly; it // delegates that to its member BufferLoader instance. return 0; } // C++ wart; class static const definitions. See Item 2 of Effective C++ // (3rd ed). const size_t BufferLoader::DEFAULT_SIZE; bool BufferLoader::load(BufferData& data, const char* begin, const char* end) const { ptrdiff_t saved_chars = 0; if (begin != nullptr && end != nullptr) { saved_chars = end - begin; } if (saved_chars && begin != data.begin()) { std::copy(begin, end, data.begin()); } size_t bytes = readData(data.begin() + saved_chars, data.size() - saved_chars); if (bytes < (data.size() - saved_chars)) { data.resize(bytes + saved_chars); } return bytes > 0; } size_t FileLoader::readData(char* ptr, size_t size) const { size_t bytes = fread(ptr, sizeof(char), size, m_file); if (bytes < size && ferror(m_file) != 0) { std::string err(strerror(errno)); throw std::runtime_error("An error occurred: " + err); } return bytes; } size_t StreamLoader::readData(char* ptr, size_t size) const { m_stream.read(ptr, size); if (m_stream) { return size; } else { if (m_stream.bad()) { throw std::runtime_error("Error in reading stream."); } else { return m_stream.gcount(); } } } void TokenBufferList::appendBufferData(const BufferData& buffer_data) { if (m_token_buffer_list.empty()) { m_token_buffer_list.emplace_back(buffer_data, 0); return; } TokenBuffer& previous_buffer = m_token_buffer_list.back(); size_t next_index = m_begin.size(); // If the previous buffer stored no values, throw it away. if (previous_buffer.first_value == previous_buffer.last_value) { m_token_buffer_list.pop_back(); } m_token_buffer_list.emplace_back(buffer_data, next_index); } void TokenBufferList::getData(size_t index, const char** const data, size_t* const length) const { // If this isn't true it means that getData was called before data // collection was complete, which is an incorrect use of the API. assert(m_begin.size() == m_end.size()); auto token_buffer_iter = m_token_buffer_list.begin(); while (index >= token_buffer_iter->last_value) { ++token_buffer_iter; // This indicates a problem in the token buffer logic - that the // index being searched for was not part of the BufferStringList. assert(token_buffer_iter != m_token_buffer_list.end()); } *length = m_end[index] - m_begin[index]; *data = token_buffer_iter->buffer_data.begin() + m_begin[index]; } } // namespace schrodinger maeparser-1.3.1/Buffer.hpp000066400000000000000000000223501436653602000154240ustar00rootroot00000000000000#ifndef _BUFFERED_READER_HPP #define _BUFFERED_READER_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include "MaeParserConfig.hpp" namespace schrodinger { /** * A simple data class to hold unchanging character buffer data. Copies are * reference counted. */ class EXPORT_MAEPARSER BufferData { private: std::vector m_data; size_t m_size; public: explicit BufferData(size_t size = 0); /** * Return access to the beginning of the data buffer for loading. */ char* begin() { return m_data.data(); } /** * Return a pointer to the beginning of the character buffer. */ const char* begin() const { return m_data.data(); } /** * Return the logical size of the buffer. */ size_t size() const { return m_size; } /** * Reduce (but not increase) logical size of the buffer (to keep track of * size). * * Specifying a size larger than the current one throws a runtime_error. * * This doesn't actually free up any memory or modify the underlying * character buffer. */ void resize(size_t size); }; /** * Base class for loading BufferData objects from some source. */ class BufferLoader { private: size_t m_default_size; public: static const size_t DEFAULT_SIZE = 131072; explicit BufferLoader(size_t default_size = DEFAULT_SIZE) : m_default_size(default_size) { } virtual ~BufferLoader() = default; /** * Return the default buffer size for this BufferLoader. This should be * used to construct the BufferData object if no other size is preferred. */ virtual size_t getDefaultSize() const { return m_default_size; } /** * Load the next chunk of data into the BufferData object. * * The amount of data loaded will be a maximum of the current size of * BufferData. * * Returns true if new characters were loaded. */ bool load(BufferData& data) const { char* begin = nullptr; char* end = nullptr; return load(data, begin, end); } /** * Copy everything from 'begin' through 'end' to the beginning of the * BufferData object (to deal with tokens that span buffer boundaries) and * then load the next chunk of data into the remainder of the BufferData. * * Returns true if new characters were loaded. */ virtual bool load(BufferData& data, const char* begin, const char* end) const; protected: /** * Read 'size' bytes and dump them into 'ptr'. Return the number of bytes * loaded. */ virtual size_t readData(char* ptr, size_t size) const = 0; }; /** * A BufferLoader that reads from an input stream. * * Note that the input stream is not owned by the StreamLoader (and so * ifstreams must be closed by the caller). */ class StreamLoader : public BufferLoader { private: std::istream& m_stream; public: StreamLoader(std::istream& stream) : m_stream(stream) {} StreamLoader() = delete; StreamLoader(const StreamLoader&) = delete; StreamLoader& operator=(const StreamLoader&) = delete; size_t readData(char* ptr, size_t size) const override; }; /** * A BufferLoader that reads from a FILE pointer. * * Experiments on OS X show this to be measurably faster than input stream * reading. While it seems this isn't expected in theory, no way to make them * equivalent has yet been found in practice. * * Note that the FILE pointer is not owned by the FileLoader and must be closed * by the caller. */ class FileLoader : public BufferLoader { private: FILE* m_file; public: FileLoader(FILE* file) : m_file(file) {} size_t readData(char* ptr, size_t size) const override; }; /** * Character buffer. * * This is a shared resource manager for an allocated character buffer along * with iterator location information. * * The character buffer is stored in a BufferData object, which is reference * counted and can be retrieved by the data() method. */ class EXPORT_MAEPARSER Buffer { private: BufferData m_data; BufferLoader* m_loader{nullptr}; size_t m_starting_column{1}; public: char* begin{nullptr}; char* end{nullptr}; char* current{nullptr}; size_t line_number{1}; explicit Buffer(size_t buffer_size = 0); /** * Construct an empty buffer that can be loaded from the provided input * stream. */ explicit Buffer(std::istream& stream, size_t buffer_size = 0); /** * Construct an empty buffer that can be loaded from the provided FILE * pointer. */ explicit Buffer(FILE* file, size_t buffer_size = 0); /** * Create a buffer from a string. * * This makes a copy of the string data. Calls to load() always return * false. */ explicit Buffer(const std::string& str); ~Buffer(); Buffer(const Buffer&) = delete; Buffer& operator=(const Buffer&) = delete; public: void setBufferLoader(BufferLoader* loader) { m_loader = loader; } BufferLoader* getBufferLoader() { return m_loader; } /** * Load new BufferData from the BufferLoader. Update Buffer pointers. */ bool load() { char* save = nullptr; return load(save); } /** * Save data from the 'save' pointer to the end of the current BufferData * into a new BufferData instance, then load the remainder of the * BufferData instance with data from the BufferLoader. * * This allows us to deal with tokens that cross buffer boundaries. * * Update Buffer pointers. */ bool load(char*& save); inline size_t size() const { return m_data.size(); } BufferData data() const { return m_data; } inline bool operator==(const Buffer& other) const { return this->m_data.begin() == other.m_data.begin(); } inline bool operator!=(const Buffer& other) const { return !(*this == other); } friend std::ostream& operator<<(std::ostream& os, const Buffer& b); /** * Return the column number of the last character read. */ size_t getColumn() const { return getColumn(current); } /** * Return the column number of the provided character. */ size_t getColumn(const char* ptr) const; }; /** * Allow Buffer objects to be written to a stream. * * This purpose of this is only to allow Boost testing assertions to work. It * doesn't do anything terribly useful. */ std::ostream& operator<<(std::ostream& os, const Buffer& b); /** * A class to collect tokens with minimal copying by saving the buffer and * token start and end indices. */ class EXPORT_MAEPARSER TokenBufferList { public: /// A simple data class to keep the info about the buffers and tokens // straight. class EXPORT_MAEPARSER TokenBuffer { public: BufferData buffer_data; /// The index of the first token stored in this buffer. size_t first_value; /// One greater than the index of the last token stored in this buffer. size_t last_value; TokenBuffer(BufferData data, size_t next_index) : buffer_data(std::move(data)), first_value(next_index), last_value(next_index) { } }; private: /// List of TokenBuffer objects. std::list m_token_buffer_list; /// Buffer indices for the beginnings of collected tokens. std::vector m_begin; /// Buffer indices for one past the end of collected tokens. std::vector m_end; public: TokenBufferList() : m_token_buffer_list(), m_begin(), m_end() {} void reserve(size_t size) { m_begin.reserve(size); m_end.reserve(size); } inline void setTokenIndices(size_t begin, size_t end) { m_begin.push_back(begin); m_end.push_back(end); m_token_buffer_list.back().last_value = m_end.size(); } void appendBufferData(const BufferData& buffer_data); /** * Return token data as char pointer and length. * * Data is owned by the TokenBufferList and does not need to be freed. * * No trailing '\0' is present; data length must be observed. */ void getData(size_t index, const char** const data, size_t* const length) const; }; /** * A class to modify a Buffer's loading behavior through RAII. * * It collects all loaded BufferData instances and stores them in a * TokenBufferList for parsing later. */ class BufferDataCollector : public BufferLoader { private: Buffer* m_buffer; BufferLoader* m_loader; TokenBufferList* m_tokens_list; public: BufferDataCollector(Buffer* buffer, TokenBufferList* tokens_list) : m_loader(nullptr), m_tokens_list(tokens_list) { m_buffer = buffer; m_loader = m_buffer->getBufferLoader(); m_buffer->setBufferLoader(this); } ~BufferDataCollector() override { m_buffer->setBufferLoader(m_loader); } bool load(BufferData& data, const char* begin, const char* end) const override; size_t readData(char* ptr, size_t size) const override; }; } // end namespace schrodinger #endif maeparser-1.3.1/CMakeLists.txt000066400000000000000000000060111436653602000162360ustar00rootroot00000000000000set (CMAKE_CXX_STANDARD 11) cmake_minimum_required(VERSION 3.5) project(maeparser) option(MAEPARSER_RIGOROUS_BUILD "Abort the build if the compiler issues any warnings" OFF ) option(MAEPARSER_BUILD_TESTS "Whether test executables should be built" ON) option(MAEPARSER_BUILD_SHARED_LIBS "Build maeparser as a shared library (turn off for a static one)" ON) if(MSVC) # C4251 disables warnings for export STL containers as arguments (returning a vector of things) add_compile_options(/wd4251 /wd4275 /wd4996 /D_SCL_SECURE_NO_WARNINGS /D_CRT_SECURE_NO_WARNINGS) add_definitions(-DBOOST_ALL_NO_LIB) endif(MSVC) if(MAEPARSER_RIGOROUS_BUILD) if(MSVC) add_compile_options(/WX) else(MSVC) add_compile_options(-Wall -Wextra -Werror) endif(MSVC) endif(MAEPARSER_RIGOROUS_BUILD) file(GLOB mae_sources "*.cpp") if(MAEPARSER_BUILD_SHARED_LIBS) add_library(maeparser SHARED ${mae_sources}) target_compile_definitions(maeparser PRIVATE "IN_MAEPARSER") target_compile_definitions(maeparser PRIVATE "BOOST_ALL_DYN_LINK") set_property(TARGET maeparser PROPERTY CXX_VISIBILITY_PRESET "hidden") else(MAEPARSER_BUILD_SHARED_LIBS) set(Boost_USE_STATIC_LIBS ON) if(NOT MSVC) set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") endif() add_library(maeparser STATIC ${mae_sources}) target_compile_definitions(maeparser PRIVATE "STATIC_MAEPARSER") endif(MAEPARSER_BUILD_SHARED_LIBS) find_package(Boost COMPONENTS iostreams REQUIRED) include_directories(${Boost_INCLUDE_DIRS}) set(boost_libs ${Boost_LIBRARIES}) find_package(Boost COMPONENTS zlib QUIET) if(Boost_ZLIB_FOUND) set(boost_libs ${boost_libs} ${Boost_LIBRARIES}) message(STATUS "Using Boost zlib module for iostreams dependency.") else(Boost_ZLIB_FOUND) find_package(ZLIB REQUIRED) set(boost_libs ${boost_libs} ${ZLIB_LIBRARIES}) message(STATUS "Using zlib library for iostreams dependency.") endif(Boost_ZLIB_FOUND) target_link_libraries(maeparser ${boost_libs}) SET_TARGET_PROPERTIES (maeparser PROPERTIES VERSION 1.3.1 SOVERSION 1 ) target_include_directories(maeparser PUBLIC $ $) if(MSVC) set(CMAKE_INSTALL_LIBDIR lib) set(CMAKE_INSTALL_BINDIR bin) else(MSVC) include(GNUInstallDirs) endif(MSVC) install(TARGETS maeparser EXPORT maeparser-targets LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}) INSTALL(EXPORT maeparser-targets FILE ${PROJECT_NAME}-config.cmake DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake) file(GLOB mae_headers "*.hpp") install(FILES ${mae_headers} DESTINATION include/maeparser) # Tests if (MAEPARSER_BUILD_TESTS) set(MEMORYCHECK_COMMAND_OPTIONS "${MEMORYCHECK_COMMAND_OPTIONS} --tool=memcheck \ --time-stamp=yes --num-callers=20 --gen-suppressions=all --leak-check=full \ --show-reachable=yes --trace-children=yes --error-exitcode=29") include(CTest) add_subdirectory(test) endif() maeparser-1.3.1/LICENSE.txt000066400000000000000000000020411436653602000153200ustar00rootroot00000000000000Copyright 2017 Schrödinger, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. maeparser-1.3.1/MaeBlock.cpp000066400000000000000000000262751436653602000156750ustar00rootroot00000000000000#include "MaeBlock.hpp" #include #include #include "MaeParser.hpp" using namespace std; namespace schrodinger { namespace mae { namespace { const double tolerance = 0.00001; // Tolerance to match string cutoff // Wrap to-string to allow it to take strings and be a no-op template inline string local_to_string(T val) { return to_string(val); } inline bool char_requires_escaping(char c) { return c == '"' || c == '\\'; } string local_to_string(const string& val) { if (val.empty()) { return R"("")"; } // Quotes are required if any character needs escaping, or there are // spaces in the string (spaces do not require escaping) bool quotes_required = false; for (const char& c : val) { if (char_requires_escaping(c) || c == ' ') { quotes_required = true; break; } } if (!quotes_required) { return val; } std::stringstream new_string; new_string << '\"'; for (const char& c : val) { if (char_requires_escaping(c)) { new_string << '\\'; } new_string << c; } new_string << '\"'; return new_string.str(); } template inline void output_property_names(ostream& out, const string& indentation, const map& properties) { for (const auto& p : properties) { out << indentation << p.first << "\n"; } } template inline void output_property_values(ostream& out, const string& indentation, const map& properties) { for (const auto& p : properties) { out << indentation << local_to_string(p.second) << "\n"; } } template void output_indexed_property_values(ostream& out, const map& properties, unsigned int index) { for (const auto& p : properties) { const auto& property = p.second; if (property->isDefined(index)) { out << ' ' << local_to_string(property->at(index)); } else { out << " <>"; } } } template bool maps_indexed_props_equal(const T& lmap, const T& rmap) { if (rmap.size() != lmap.size()) return false; auto diff = std::mismatch( lmap.begin(), lmap.end(), rmap.begin(), [](decltype(*begin(lmap)) l, decltype(*begin(lmap)) r) { return l.first == r.first && *(l.second) == *(r.second); }); if (diff.first != lmap.end()) return false; return true; } } // namespace void Block::write(ostream& out, unsigned int current_indentation) const { string root_indentation = string(current_indentation, ' '); current_indentation += 2; string indentation = string(current_indentation, ' '); const bool has_data = !m_bmap.empty() || !m_rmap.empty() || !m_imap.empty() || !m_smap.empty(); out << root_indentation << getName() << " {\n"; if (has_data) { output_property_names(out, indentation, m_bmap); output_property_names(out, indentation, m_rmap); output_property_names(out, indentation, m_imap); output_property_names(out, indentation, m_smap); out << indentation + ":::\n"; output_property_values(out, indentation, m_bmap); output_property_values(out, indentation, m_rmap); output_property_values(out, indentation, m_imap); output_property_values(out, indentation, m_smap); } if (hasIndexedBlockData()) { const auto block_names = m_indexed_block_map->getBlockNames(); for (const auto& name : block_names) { const auto& indexed_block = m_indexed_block_map->getIndexedBlock(name); indexed_block->write(out, current_indentation); } } for (const auto& p : m_sub_block) { const auto& sub_block = p.second; sub_block->write(out, current_indentation); } out << root_indentation << "}\n\n"; return; } string Block::toString() const { ostringstream stream; write(stream); return stream.str(); } shared_ptr Block::getIndexedBlock(const string& name) const { if (!hasIndexedBlockData()) { throw out_of_range("Indexed block not found: " + name); } return const_pointer_cast( m_indexed_block_map->getIndexedBlock(name)); } bool real_map_equal(const map& rmap1, const map& rmap2) { if (rmap1.size() != rmap2.size()) return false; for (const auto& p : rmap1) { if (rmap2.count(p.first) != 1) return false; if ((float) abs(p.second - rmap2.at(p.first)) > tolerance) return false; } return true; } bool Block::operator==(const Block& rhs) const { if (m_bmap != rhs.m_bmap) return false; if (!real_map_equal(m_rmap, rhs.m_rmap)) return false; if (m_imap != rhs.m_imap) return false; if (m_smap != rhs.m_smap) return false; if (m_sub_block != rhs.m_sub_block) return false; if (!(*m_indexed_block_map == *(rhs.m_indexed_block_map))) return false; return true; } bool IndexedBlockMapI::operator==(const IndexedBlockMapI& rhs) const { const auto& block_names = getBlockNames(); for (const auto& name : block_names) { if (!rhs.hasIndexedBlock(name)) return false; const auto& block1 = rhs.getIndexedBlock(name); const auto& block2 = getIndexedBlock(name); if (*block1 != *block2) return false; } return true; } bool IndexedBlockMap::hasIndexedBlock(const string& name) const { return m_indexed_block.find(name) != m_indexed_block.end(); } shared_ptr IndexedBlockMap::getIndexedBlock(const string& name) const { auto block_iter = m_indexed_block.find(name); if (block_iter != m_indexed_block.end()) { return const_pointer_cast(block_iter->second); } else { throw out_of_range("Indexed block not found: " + name); } } bool BufferedIndexedBlockMap::hasIndexedBlock(const string& name) const { if (m_indexed_buffer.find(name) != m_indexed_buffer.end()) { return true; } else if (m_indexed_block.find(name) != m_indexed_block.end()) { return true; } else { return false; } } shared_ptr BufferedIndexedBlockMap::getIndexedBlock(const string& name) const { auto itb = m_indexed_block.find(name); if (itb != m_indexed_block.end()) { return itb->second; } auto itbb = m_indexed_buffer.find(name); if (itbb == m_indexed_buffer.end()) { throw out_of_range("Indexed block not found: " + name); } else { shared_ptr ib(itbb->second->getIndexedBlock()); return ib; } } template <> EXPORT_MAEPARSER void IndexedBlock::setProperty(const string& name, shared_ptr value) { set_indexed_property(m_bmap, name, std::move(value)); } template <> EXPORT_MAEPARSER void IndexedBlock::setProperty(const string& name, shared_ptr> value) { set_indexed_property>(m_rmap, name, std::move(value)); } template <> EXPORT_MAEPARSER void IndexedBlock::setProperty(const string& name, shared_ptr> value) { set_indexed_property>(m_imap, name, std::move(value)); } template <> EXPORT_MAEPARSER void IndexedBlock::setProperty(const string& name, shared_ptr> value) { set_indexed_property>(m_smap, name, std::move(value)); } size_t IndexedBlock::size() const { size_t count = 0; // To save memory, not all maps will have max index count for the block, // so we must find the max size of all maps in the block. for (const auto& p : m_bmap) count = max(p.second->size(), count); for (const auto& p : m_imap) count = max(p.second->size(), count); for (const auto& p : m_rmap) count = max(p.second->size(), count); for (const auto& p : m_smap) count = max(p.second->size(), count); return count; } void IndexedBlock::write(ostream& out, unsigned int current_indentation) const { string root_indentation = string(current_indentation, ' '); string indentation = string(current_indentation + 2, ' '); const bool has_data = !m_bmap.empty() || !m_rmap.empty() || !m_imap.empty() || !m_smap.empty(); out << root_indentation << getName() << "[" << to_string((int) size()) << "] {\n"; if (has_data) { out << indentation + "# First column is Index #\n"; output_property_names(out, indentation, m_bmap); output_property_names(out, indentation, m_rmap); output_property_names(out, indentation, m_imap); output_property_names(out, indentation, m_smap); out << indentation + ":::\n"; for (unsigned int i = 0; i < size(); ++i) { out << indentation << i + 1; output_indexed_property_values(out, m_bmap, i); output_indexed_property_values(out, m_rmap, i); output_indexed_property_values(out, m_imap, i); output_indexed_property_values(out, m_smap, i); out << endl; } out << indentation + ":::\n"; } out << root_indentation << "}\n"; return; } string IndexedBlock::toString() const { ostringstream stream; write(stream); return stream.str(); } template bool IndexedProperty::operator==(const IndexedProperty& rhs) const { if (m_is_null == nullptr || rhs.m_is_null == nullptr) { if ((m_is_null == nullptr) != (rhs.m_is_null == nullptr)) return false; } else if (*m_is_null != *(rhs.m_is_null)) { return false; } if (m_data != rhs.m_data) return false; return true; } // For doubles we need to implement our own comparator for the vectors to // take precision into account template <> bool IndexedProperty::operator==( const IndexedProperty& rhs) const { if (m_is_null == nullptr || rhs.m_is_null == nullptr) { if ((m_is_null == nullptr) != (rhs.m_is_null == nullptr)) return false; } else if (*m_is_null != *(rhs.m_is_null)) return false; for (size_t i = 0; i < m_data.size(); ++i) if ((float) abs(m_data[i] - rhs.m_data[i]) > tolerance) return false; return true; } bool IndexedBlock::operator==(const IndexedBlock& rhs) const { if (!maps_indexed_props_equal(m_bmap, rhs.m_bmap)) return false; if (!maps_indexed_props_equal(m_imap, rhs.m_imap)) return false; if (!maps_indexed_props_equal(m_rmap, rhs.m_rmap)) return false; if (!maps_indexed_props_equal(m_smap, rhs.m_smap)) return false; return true; } } // namespace mae } // namespace schrodinger maeparser-1.3.1/MaeBlock.hpp000066400000000000000000000357531436653602000157030ustar00rootroot00000000000000#pragma once #include #include #include #include #include #include #include #include "MaeParserConfig.hpp" namespace schrodinger { namespace mae { using BoolProperty = uint8_t; template inline const T& get_property(const std::map& map, const std::string& name) { auto iter = map.find(name); if (iter == map.end()) { throw std::out_of_range("Key not found: " + name); } else { return iter->second; } } // Forward declaration. class IndexedBlockBuffer; class IndexedBlock; class EXPORT_MAEPARSER IndexedBlockMapI { public: virtual ~IndexedBlockMapI() = default; virtual bool hasIndexedBlock(const std::string& name) const = 0; virtual std::shared_ptr getIndexedBlock(const std::string& name) const = 0; virtual std::vector getBlockNames() const = 0; bool operator==(const IndexedBlockMapI& rhs) const; }; class EXPORT_MAEPARSER IndexedBlockMap : public IndexedBlockMapI { std::map> m_indexed_block; public: bool hasIndexedBlock(const std::string& name) const override; std::shared_ptr getIndexedBlock(const std::string& name) const override; std::vector getBlockNames() const override { std::vector rval; for (const auto& p : m_indexed_block) { rval.push_back(p.first); } return rval; } /** * Add an IndexedBlock to the map. */ void addIndexedBlock(const std::string& name, std::shared_ptr indexed_block) { m_indexed_block[name] = std::move(indexed_block); } }; class EXPORT_MAEPARSER BufferedIndexedBlockMap : public IndexedBlockMapI { private: std::map> m_indexed_block; std::map> m_indexed_buffer; public: bool hasIndexedBlock(const std::string& name) const override; std::shared_ptr getIndexedBlock(const std::string& name) const override; std::vector getBlockNames() const override { std::vector rval; for (const auto& p : m_indexed_buffer) { rval.push_back(p.first); } return rval; } /** * Add an IndexedBlockBuffer to the map, which can be used to retrieve an * IndexedBlock. */ void addIndexedBlockBuffer(const std::string& name, std::shared_ptr block_buffer) { m_indexed_buffer[name] = std::move(block_buffer); } }; class EXPORT_MAEPARSER Block { private: const std::string m_name; std::map m_bmap; std::map m_rmap; std::map m_imap; std::map m_smap; std::map> m_sub_block; std::shared_ptr m_indexed_block_map; public: // Prevent copying. Block(const Block&) = delete; Block& operator=(const Block&) = delete; Block(std::string name) : m_name(std::move(name)), m_bmap(), m_rmap(), m_imap(), m_smap(), m_indexed_block_map(nullptr) { } const std::string& getName() const { return m_name; } std::string toString() const; void write(std::ostream& out, unsigned int current_indentation = 0) const; void setIndexedBlockMap(std::shared_ptr indexed_block_map) { m_indexed_block_map = std::move(indexed_block_map); } bool hasIndexedBlockData() const { return m_indexed_block_map != nullptr; } bool hasIndexedBlock(const std::string& name) { return hasIndexedBlockData() && m_indexed_block_map->hasIndexedBlock(name); } std::shared_ptr getIndexedBlock(const std::string& name) const; void addBlock(std::shared_ptr b) { m_sub_block[b->getName()] = std::move(b); } /** * Check whether this block has a sub-block of the provided name. */ bool hasBlock(const std::string& name) { std::map>::const_iterator iter = m_sub_block.find(name); return (iter != m_sub_block.end()); } /** * Retrieve a shared pointer to the named sub-block. */ std::shared_ptr getBlock(const std::string& name) const { std::map>::const_iterator iter = m_sub_block.find(name); if (iter == m_sub_block.end()) { throw std::out_of_range("Sub-block not found: " + name); } else { return iter->second; } } /** * Get the names of all non-indexed sub-blocks */ std::vector getBlockNames() const { std::vector names; for (auto& n : m_sub_block) { names.push_back(n.first); } return names; } /** * Get the names of all indexed sub-blocks */ std::vector getIndexedBlockNames() const { return m_indexed_block_map->getBlockNames(); } bool operator==(const Block& rhs) const; bool hasRealProperty(const std::string& name) const { return (m_rmap.find(name) != m_rmap.end()); } double getRealProperty(const std::string& name) const { return get_property(m_rmap, name); } void setRealProperty(const std::string& name, double value) { m_rmap[name] = value; } bool hasIntProperty(const std::string& name) const { return (m_imap.find(name) != m_imap.end()); } int getIntProperty(const std::string& name) const { return get_property(m_imap, name); } void setIntProperty(const std::string& name, int value) { m_imap[name] = value; } bool hasBoolProperty(const std::string& name) const { return (m_bmap.find(name) != m_bmap.end()); } bool getBoolProperty(const std::string& name) const { return 1u == get_property(m_bmap, name); } void setBoolProperty(const std::string& name, bool value) { m_bmap[name] = static_cast(value); } bool hasStringProperty(const std::string& name) const { return (m_smap.find(name) != m_smap.end()); } const std::string& getStringProperty(const std::string& name) const { return get_property(m_smap, name); } void setStringProperty(const std::string& name, std::string value) { m_smap[name] = std::move(value); } template const std::map& getProperties() const; }; template class IndexedProperty { private: std::vector m_data; boost::dynamic_bitset<>* m_is_null; public: // Prevent copying. IndexedProperty(const IndexedProperty&) = delete; IndexedProperty& operator=(const IndexedProperty&) = delete; using size_type = typename std::vector::size_type; /** * Construct an IndexedProperty from a reference to a vector of data. * This swaps out the data of the input vector. * * The optional boost::dynamic_bitset is owned by the created object. */ explicit IndexedProperty(std::vector& data, boost::dynamic_bitset<>* is_null = nullptr) : m_data(), m_is_null(is_null) { m_data.swap(data); } ~IndexedProperty() { if (m_is_null != nullptr) { delete m_is_null; } } bool operator==(const IndexedProperty& rhs) const; size_type size() const { return m_data.size(); } bool hasUndefinedValues() const { return (m_is_null != NULL && m_is_null->any()); } bool isDefined(size_type index) const { if (m_is_null == nullptr) { // Use of assert matches out-of-bounds behavior for dynamic_bitset. assert(index < m_data.size()); return true; } else { return !m_is_null->test(index); } } void undefine(size_type index) { if (m_is_null == NULL) { m_is_null = new boost::dynamic_bitset<>(m_data.size()); } m_is_null->set(index); } inline T& operator[](size_type index) { if (m_is_null && m_is_null->test(index)) { throw std::runtime_error("Indexed property value undefined."); } return m_data[index]; } inline const T& operator[](size_type index) const { if (m_is_null && m_is_null->test(index)) { throw std::runtime_error("Indexed property value undefined."); } return m_data[index]; } inline T& at(size_type index) { return operator[](index); } inline const T& at(size_type index) const { return operator[](index); } inline const T& at(size_type index, const T& default_) const { if (m_is_null && m_is_null->test(index)) { return default_; } return m_data[index]; } void set(size_type index, const T& value) { m_data[index] = value; if (m_is_null != NULL && m_is_null->test(index)) { m_is_null->reset(index); } } const std::vector& data() const { return m_data; } const boost::dynamic_bitset<>* nullIndices() const { return m_is_null; } }; using IndexedRealProperty = IndexedProperty; using IndexedIntProperty = IndexedProperty; using IndexedBoolProperty = IndexedProperty; using IndexedStringProperty = IndexedProperty; template inline std::shared_ptr get_indexed_property(const std::map>& map, const std::string& name) { auto iter = map.find(name); if (iter == map.end()) { return std::shared_ptr(nullptr); } else { return iter->second; } } template inline void set_indexed_property(std::map>& map, const std::string& name, std::shared_ptr value) { map[name] = std::move(value); } class EXPORT_MAEPARSER IndexedBlock { private: const std::string m_name; std::map> m_bmap; std::map> m_imap; std::map> m_rmap; std::map> m_smap; public: // Prevent copying. IndexedBlock(const IndexedBlock&) = delete; IndexedBlock& operator=(const IndexedBlock&) = delete; /** * Create an indexed block. */ IndexedBlock(std::string name) : m_name(std::move(name)), m_bmap(), m_imap(), m_rmap(), m_smap() { } size_t size() const; const std::string& getName() const { return m_name; } std::string toString() const; void write(std::ostream& out, unsigned int current_indentation = 0) const; bool operator==(const IndexedBlock& rhs) const; bool operator!=(const IndexedBlock& rhs) const { return !(operator==(rhs)); } template void setProperty(const std::string& name, std::shared_ptr> value); bool hasBoolProperty(const std::string& name) const { return (m_bmap.find(name) != m_bmap.end()); } std::shared_ptr getBoolProperty(const std::string& name) const { return get_indexed_property(m_bmap, name); } void setBoolProperty(const std::string& name, std::shared_ptr value) { set_indexed_property(m_bmap, name, std::move(value)); } bool hasIntProperty(const std::string& name) const { return (m_imap.find(name) != m_imap.end()); } std::shared_ptr getIntProperty(const std::string& name) const { return get_indexed_property(m_imap, name); } void setIntProperty(const std::string& name, std::shared_ptr value) { set_indexed_property(m_imap, name, std::move(value)); } bool hasRealProperty(const std::string& name) const { return (m_rmap.find(name) != m_rmap.end()); } std::shared_ptr getRealProperty(const std::string& name) const { return get_indexed_property(m_rmap, name); } void setRealProperty(const std::string& name, std::shared_ptr value) { set_indexed_property(m_rmap, name, std::move(value)); } bool hasStringProperty(const std::string& name) const { return (m_smap.find(name) != m_smap.end()); } std::shared_ptr getStringProperty(const std::string& name) const { return get_indexed_property(m_smap, name); } void setStringProperty(const std::string& name, std::shared_ptr value) { set_indexed_property(m_smap, name, std::move(value)); } template const std::map>>& getProperties() const; }; // Template specializations template <> inline const std::map& Block::getProperties() const { return m_bmap; } template <> inline const std::map& Block::getProperties() const { return m_imap; } template <> inline const std::map& Block::getProperties() const { return m_rmap; } template <> inline const std::map& Block::getProperties() const { return m_smap; } template <> inline const std::map>>& IndexedBlock::getProperties() const { return m_bmap; } template <> inline const std::map>>& IndexedBlock::getProperties() const { return m_imap; } template <> inline const std::map>>& IndexedBlock::getProperties() const { return m_rmap; } template <> inline const std::map>>& IndexedBlock::getProperties() const { return m_smap; } } // namespace mae } // namespace schrodinger maeparser-1.3.1/MaeConstants.hpp000066400000000000000000000054041436653602000166130ustar00rootroot00000000000000#pragma once #include namespace schrodinger { namespace mae { const char* const MAE_FORMAT_VERSION = "s_m_m2io_version"; const char* const CT_BLOCK = "f_m_ct"; const char* const CT_TITLE = "s_m_title"; const char* const ATOM_BLOCK = "m_atom"; const char* const ATOM_ATOMIC_NUM = "i_m_atomic_number"; const char* const ATOM_X_COORD = "r_m_x_coord"; const char* const ATOM_Y_COORD = "r_m_y_coord"; const char* const ATOM_Z_COORD = "r_m_z_coord"; const char* const ATOM_FORMAL_CHARGE = "i_m_formal_charge"; const char* const ATOM_PARTIAL_CHARGE = "r_m_charge1"; const char* const BOND_BLOCK = "m_bond"; const char* const BOND_ATOM_1 = "i_m_from"; const char* const BOND_ATOM_2 = "i_m_to"; const char* const BOND_ORDER = "i_m_order"; const char* const DEPEND_BLOCK = "m_depend"; /** * These are the prefixes used to store stereochemical properties in Maestro * files. Each of those properties will be associated to one of these prefixes * plus an integer (integers don't have any particular meaning, they just * prevent the property names from clashing), e.g.: * * s_st_Chirality_1 : 2_R_1_3_5_4 * s_st_Chirality_2 : 6_ANR_1_5_7 * * Syntax of chirality and pseudochirality properties is the same: * 1. Index of the chiral/pseudochiral atom. * 2. Chiral/pseudochiral label (R/S or ANR/ANS). * 3. Sequence of atoms around the chiral one, ordered by decreasing * Cahn-Ingold-Prelog rank, or atom index, in the case of ANR/ANS. * Elements are separated by underscores. For cases in which only 3 neighboring * indexes are specified, an implicit Hydrogen atom needs to be considered. * * * s_st_EZ_2 : 1_2_3_4_E * * Syntax for bond stereochemistry properties is similar: * 1. Highest C-I-P ranked stereogenical atom at one side of the group. * 2. List of intermediate atoms in the stereochemical group. These are * typically 2 atoms bonded by a double bond, but can be more atoms and not * necessarily bonded by double bonds (e.g. in case of allenes or * allene-like structures) * 3. Highest C-I-P ranked stereogenical atom at the other side of the group. * 4. Stereochemical label for the bond (E/Z) * As in chiral/pseudochiral properties, values are separated by underscores. */ const std::string CT_CHIRALITY_PROP_PREFIX = "s_st_Chirality_"; const std::string CT_EZ_PROP_PREFIX = "s_st_EZ_"; /** * This prefix has been deprecated, and will not be used in future Schrodinger * Suite releases. It is included in this header only for the purpose of * documentation and backwards compatibility. The format of the property string * is identical as the one presented in the example for chirality. */ const std::string CT_PSEUDOCHIRALITY_PROP_PREFIX = "s_st_AtomNumChirality_"; } // End namespace mae } // End namespace schrodinger maeparser-1.3.1/MaeParser.cpp000066400000000000000000000566721436653602000161030ustar00rootroot00000000000000#include #include #include #include #include "MaeBlock.hpp" #include "MaeParser.hpp" #define WHITESPACE ' ' : case '\n' : case '\r' : case '\t' namespace qi = boost::spirit::qi; namespace schrodinger { namespace mae { static bool property_key_author_name(Buffer& buffer, char*& save); static std::string outer_block_name(Buffer& buffer); void read_exception::format(size_t line_number, size_t column, const char* msg) { #ifdef _MSC_VER _snprintf(m_msg, MAEPARSER_EXCEPTION_BUFFER_SIZE, "Line %Iu, column %Iu: %s\n", #else snprintf(m_msg, MAEPARSER_EXCEPTION_BUFFER_SIZE, "Line %zu, column %zu: %s\n", #endif line_number, column, msg); m_msg[MAEPARSER_EXCEPTION_BUFFER_SIZE - 1] = '\0'; } // TODO: Not sure that newlines embedded in comments are allowed. void comment(Buffer& buffer) { ++buffer.current; // Step past initial '#' while (buffer.current < buffer.end || buffer.load()) { switch (*buffer.current) { case '#': return; case '\n': ++buffer.line_number; } ++buffer.current; } throw read_exception(buffer, "Unterminated comment."); } void whitespace(Buffer& buffer) { while (buffer.current < buffer.end || buffer.load()) { switch (*buffer.current) { case '\n': ++buffer.line_number; case '\r': case ' ': case '\t': break; case '#': comment(buffer); break; default: return; } ++buffer.current; } } bool character(char c, Buffer& buffer) { char* save = nullptr; return character(c, buffer, save); } bool character(char c, Buffer& buffer, char*& save) { if (buffer.current >= buffer.end && !buffer.load(save)) { return false; } else if (*buffer.current != c) { return false; } else { ++buffer.current; return true; } } static void remove_escape_characters(std::string& s) { size_t j = 0; for (size_t i = 0; i < s.size(); ++i, ++j) { if (s[i] == '\\') ++i; if (j < i) s[j] = s[i]; } s.resize(j); } /** * Read an integer and return its value. An integer is terminated * either by whitespace or a ']'. */ template <> EXPORT_MAEPARSER int parse_value(Buffer& buffer) { int value = 0; int sign = 1; char* save = buffer.current; while (buffer.current < buffer.end || buffer.load()) { switch (*buffer.current) { case ']': case WHITESPACE: if (save == buffer.current) { throw read_exception(buffer, "Missing integer."); } return value * sign; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = value * 10 + *buffer.current - '0'; break; case '-': if (sign == -1 || value) { throw read_exception(buffer, "Unexpected '-'."); } sign = -1; break; default: throw read_exception(buffer, "Unexpected character."); } ++buffer.current; } return value * sign; } template <> EXPORT_MAEPARSER double parse_value(Buffer& buffer) { char* save = buffer.current; while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case '-': case '.': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case 'e': case 'E': break; case WHITESPACE: goto done; default: throw read_exception(buffer, "Unexpected character in real number."); } ++buffer.current; } done: if (save == buffer.current) { throw read_exception(buffer, "Missing real."); } double value = 0; if (!qi::parse(save, buffer.current, qi::double_, value) || save != buffer.current) { // On error, save will have advanced to the point of the problem. // May differ on versions of boost throw read_exception(buffer.line_number, buffer.getColumn(save), "Bad real number."); } return value; } template <> EXPORT_MAEPARSER std::string parse_value(Buffer& buffer) { char* save = buffer.current; if (*buffer.current != '"') { while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case WHITESPACE: return std::string(save, buffer.current); } ++buffer.current; } return std::string(save, buffer.current); } else { save = ++buffer.current; std::string rval; while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case '"': rval = std::string(save, buffer.current++); remove_escape_characters(rval); return rval; case '\\': ++buffer.current; break; } ++buffer.current; } throw read_exception(buffer, "Unterminated quoted string at EOF."); } } template <> EXPORT_MAEPARSER BoolProperty parse_value(Buffer& buffer) { bool value = false; if (*buffer.current == '1') { value = true; } else if (*buffer.current == '0') { value = false; } else { throw read_exception(buffer, "Unexpected character for boolean value."); } ++buffer.current; if (buffer.current >= buffer.end) { if (!buffer.load()) { return value; } } switch (*buffer.current) { case WHITESPACE: return value; default: throw read_exception(buffer, "Unexpected character for boolean value."); } } std::string outer_block_beginning(Buffer& buffer) { std::string name = outer_block_name(buffer); schrodinger::mae::whitespace(buffer); if (!character('{', buffer)) { throw read_exception(buffer, "Missing '{' for outer block."); } return name; } std::shared_ptr MaeParser::outerBlock() { if (!m_buffer.load()) { return nullptr; } std::string name = outer_block_beginning(m_buffer); return blockBody(name); } std::string outer_block_name(Buffer& buffer) { char* save = buffer.current; char c = *buffer.current; if (c == '{') { return std::string(); } else if (c != 'f' && c != 'p') { goto bad_format; } ++buffer.current; if (!character('_', buffer, save)) { goto bad_format; } if (!property_key_author_name(buffer, save)) { goto bad_format; } return std::string(save, buffer.current - save); bad_format: throw read_exception(buffer, "Bad format for outer block name; " "must be (f|p)__."); } std::string MaeParser::blockBeginning(int* indexed) { *indexed = 0; char* save = m_buffer.current; if (!property_key_author_name(m_buffer, save)) { throw read_exception(m_buffer, "Bad format for block name; " "must be _."); } std::string name(save, m_buffer.current - save); schrodinger::mae::whitespace(m_buffer); if (character('[', m_buffer)) { schrodinger::mae::whitespace( m_buffer); // TODO: is m_block[ 123 ] allowed? *indexed = parse_value(m_buffer); schrodinger::mae::whitespace(m_buffer); if (!character(']', m_buffer)) { throw read_exception(m_buffer, "Bad block index; missing ']'."); } schrodinger::mae::whitespace(m_buffer); } if (character('{', m_buffer)) { return name; } else { throw read_exception(m_buffer, "Missing '{' for block."); } } std::shared_ptr MaeParser::blockBody(const std::string& name) { auto block = std::make_shared(name); auto indexed_block_parser = std::shared_ptr(getIndexedBlockParser()); std::vector> property_names; schrodinger::mae::whitespace(m_buffer); properties(&property_names); for (auto& property_name : property_names) { schrodinger::mae::whitespace(m_buffer); switch ((*property_name)[0]) { case 'r': block->setRealProperty(*property_name, parse_value(m_buffer)); break; case 's': block->setStringProperty(*property_name, parse_value(m_buffer)); break; case 'i': block->setIntProperty(*property_name, parse_value(m_buffer)); break; case 'b': block->setBoolProperty(*property_name, 1u == parse_value(m_buffer)); break; } } auto advance = [this]() { schrodinger::mae::whitespace(m_buffer); if (!m_buffer.load()) { throw read_exception(m_buffer, "Missing '}' for block."); } }; int indexed = 0; for (advance(); *m_buffer.current != '}'; advance()) { std::string name = blockBeginning(&indexed); if (indexed) { indexed_block_parser->parse(name, indexed, m_buffer); } else { auto sub_block = blockBody(name); block->addBlock(std::move(sub_block)); } } ++m_buffer.current; block->setIndexedBlockMap(indexed_block_parser->getIndexedBlockMap()); return block; } void MaeParser::properties( std::vector>* property_names) { std::shared_ptr property_name; while ((property_name = property_key(m_buffer)) != nullptr) { property_names->push_back(property_name); schrodinger::mae::whitespace(m_buffer); } triple_colon(m_buffer); return; } void triple_colon(Buffer& buffer) { for (int i = 0; i < 3; ++i) { if (!character(':', buffer)) { throw read_exception(buffer, "Bad ':::' token."); } } } std::shared_ptr MaeParser::property() { return property_key(m_buffer); } std::shared_ptr property_key(Buffer& buffer) { if (!buffer.load()) { throw read_exception(buffer, "Missing property key."); } char* save = buffer.current; switch (*buffer.current) { case 'b': case 'i': case 'r': case 's': break; case ':': return nullptr; default: goto bad_format; } ++buffer.current; if (buffer.current >= buffer.end) { if (!buffer.load(save)) { goto bad_format; } } if (*buffer.current != '_') { goto bad_format; } ++buffer.current; if (!property_key_author_name(buffer, save)) { goto bad_format; } return std::make_shared(save, buffer.current - save); bad_format: throw read_exception(buffer, "Bad format for property; " "must be (b|i|r|s)__."); } bool property_key_author_name(Buffer& buffer, char*& save) { while (buffer.current < buffer.end || buffer.load(save)) { if (*buffer.current == '_') { ++buffer.current; break; } else if (!((*buffer.current >= 'a' && *buffer.current <= 'z') || (*buffer.current >= 'A' && *buffer.current <= 'Z') || (*buffer.current >= '0' && *buffer.current <= '9'))) { return false; } ++buffer.current; } char* start = buffer.current; while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case WHITESPACE: case '{': case '[': return buffer.current != start; } ++buffer.current; } return false; } void IndexedBlockBuffer::value(Buffer& buffer) { char* save = buffer.current; if (buffer.current == buffer.end) { throw read_exception(buffer, "Unexpected EOF in indexed block values."); } if (*buffer.current != '"') { while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case WHITESPACE: m_tokens_list.setTokenIndices(save - buffer.begin, buffer.current - buffer.begin); return; } ++buffer.current; } // If EOF is reached... m_tokens_list.setTokenIndices(save - buffer.begin, buffer.current - buffer.begin); return; } else { ++buffer.current; while (buffer.current < buffer.end || buffer.load(save)) { switch (*buffer.current) { case '"': if (*(buffer.current - 1) == '\\') { break; } ++buffer.current; m_tokens_list.setTokenIndices(save - buffer.begin, buffer.current - buffer.begin); return; } ++buffer.current; } throw read_exception(buffer, "Unterminated quoted string at EOF."); } } void DirectIndexedBlockParser::parse(const std::string& name, size_t size, Buffer& buffer) { if (m_indexed_block_map == nullptr) { m_indexed_block_map = std::make_shared(); } auto indexed_block = std::make_shared(name); std::vector property_keys; whitespace(buffer); std::shared_ptr property_name; while ((property_name = property_key(buffer)) != nullptr) { property_keys.push_back(*property_name); whitespace(buffer); } triple_colon(buffer); std::vector parsers; parsers.reserve(property_keys.size() + 1); IndexedValueParser* p = new IndexedValueCollector("", size); parsers.push_back(p); for (auto& key : property_keys) { switch (key[0]) { case 'b': p = new IndexedValueCollector(key, size); break; case 'i': p = new IndexedValueCollector(key, size); break; case 'r': p = new IndexedValueCollector(key, size); break; case 's': p = new IndexedValueCollector(key, size); break; default: throw std::out_of_range("An unexpected error was found."); } parsers.push_back(p); } for (size_t i = 0; i < size; ++i) { for (auto parser : parsers) { whitespace(buffer); parser->parse(buffer); } } whitespace(buffer); triple_colon(buffer); whitespace(buffer); if (!character('}', buffer)) { throw read_exception(buffer, "Missing '{' for outer block."); } for (auto parser : parsers) { parser->addToIndexedBlock(indexed_block.get()); delete parser; } m_indexed_block_map->addIndexedBlock(name, std::move(indexed_block)); } std::shared_ptr DirectIndexedBlockParser::getIndexedBlockMap() { std::shared_ptr map(m_indexed_block_map); m_indexed_block_map = nullptr; return map; } void IndexedBlockBuffer::parse(Buffer& buffer) { // Modifies buffer to use a loader that stores offsets and data in // m_tokens_list. Original loader restored at data_collector destruction. BufferDataCollector data_collector(&buffer, &m_tokens_list); size_t values = m_rows * (m_property_names.size() + 1); m_tokens_list.reserve(values); if (buffer.size() == 0) { if (!buffer.load()) { throw read_exception(buffer, "Unexpected EOF in indexed block scan."); } } m_tokens_list.appendBufferData(buffer.data()); for (std::size_t ix = 0; ix < values; ix++) { // TODO: Another boost in performance can be had by avoiding the // function call overhead for value and whitespace, but simplying // marking the functions as inline doesn't seem to do the trick. whitespace(buffer); value(buffer); } whitespace(buffer); } /** * This function is measurably faster than strtol. * * The main reason for this is probably that it does not deal with alternate * bases for the integer. */ static long int simple_strtol(const char* ptr, const char* end) { long int value = 0; long int sign = 1; while (ptr < end) { switch (*ptr) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': value = value * 10 + *ptr - '0'; break; case '-': if (sign == -1 || value) { throw std::invalid_argument("Unexpected '-' in integer."); } sign = -1; break; default: throw std::invalid_argument("Unexpected character in integer."); } ++ptr; } return value * sign; } IndexedBlock* IndexedBlockBuffer::getIndexedBlock() { auto* iblock = new IndexedBlock(getName()); std::vector::const_iterator iter = m_property_names.begin(); // Indexed blocks have row indexes explicitly mixed in as the first // value of each row. This is why a) prop_indx starts at 1, b) // col_count is prop_count + 1 instead of prop_count. // size_t prop_count = m_property_names.size(); size_t col_count = prop_count + 1; size_t value_count = col_count * m_rows; boost::dynamic_bitset<>* is_null = nullptr; for (int prop_indx = 1; iter != m_property_names.end(); ++iter, ++prop_indx) { char type = (*iter)[0]; is_null = nullptr; const char* data; size_t len; switch (type) { case 'b': { std::vector bvalues; bvalues.reserve(m_rows); for (size_t ix = prop_indx; ix < value_count; ix += col_count) { getData(ix, &data, &len); if (data[0] == '<' && data[1] == '>') { if (is_null == nullptr) { is_null = new boost::dynamic_bitset<>(m_rows); } is_null->set(bvalues.size()); bvalues.push_back(false); } else if (data[0] == '1') { bvalues.push_back(true); } else if (data[0] == '0') { bvalues.push_back(false); } else { throw std::out_of_range("Bogus bool."); } } std::shared_ptr ibp( new IndexedBoolProperty(bvalues, is_null)); iblock->setBoolProperty(*iter, ibp); } break; case 'i': { std::vector ivalues; ivalues.reserve(m_rows); for (size_t ix = prop_indx; ix < value_count; ix += col_count) { getData(ix, &data, &len); if (data[0] == '<' && data[1] == '>') { if (is_null == nullptr) { is_null = new boost::dynamic_bitset<>(m_rows); } is_null->set(ivalues.size()); ivalues.push_back(0); } else { long int value = simple_strtol(data, data + len); ivalues.push_back(value); } } std::shared_ptr iip( new IndexedIntProperty(ivalues, is_null)); iblock->setIntProperty(*iter, iip); } break; case 'r': { std::vector dvalues; dvalues.reserve(m_rows); for (size_t ix = prop_indx; ix < value_count; ix += col_count) { getData(ix, &data, &len); if (data[0] == '<' && data[1] == '>') { if (is_null == nullptr) { is_null = new boost::dynamic_bitset<>(m_rows); } is_null->set(dvalues.size()); dvalues.push_back(0); } else { double value = 0; const char* end = data + len; if (!qi::parse(data, end, qi::double_, value) || data != end) { throw std::invalid_argument("Bad floating point " "representation."); } dvalues.push_back(value); } } std::shared_ptr irp( new IndexedRealProperty(dvalues, is_null)); iblock->setRealProperty(*iter, irp); } break; case 's': { std::vector svalues; svalues.reserve(m_rows); for (size_t ix = prop_indx; ix < value_count; ix += col_count) { getData(ix, &data, &len); if (data[0] == '<' && data[1] == '>') { if (is_null == nullptr) { is_null = new boost::dynamic_bitset<>(m_rows); } is_null->set(svalues.size()); svalues.emplace_back(); } else { if (data[0] != '"') { // Check for quote wrapping svalues.emplace_back(data, len); } else { // During parsing we check for full quote wrapping auto rval = std::string(data + 1, len - 2); remove_escape_characters(rval); svalues.emplace_back(rval); } } } std::shared_ptr isp( new IndexedStringProperty(svalues, is_null)); iblock->setStringProperty(*iter, isp); } break; } } return iblock; } BufferedIndexedBlockParser::BufferedIndexedBlockParser() { m_indexed_block_map = std::make_shared(); } std::shared_ptr BufferedIndexedBlockParser::getIndexedBlockMap() { std::shared_ptr indexed_block_map(m_indexed_block_map); m_indexed_block_map = nullptr; return indexed_block_map; } void BufferedIndexedBlockParser::parse(const std::string& name, size_t size, Buffer& buffer) { auto ibb = std::make_shared(name, size); whitespace(buffer); std::shared_ptr property_name; while ((property_name = property_key(buffer)) != nullptr) { ibb->addPropertyName(std::move(*property_name)); whitespace(buffer); } triple_colon(buffer); ibb->parse(buffer); triple_colon(buffer); whitespace(buffer); if (!character('}', buffer)) { throw read_exception(buffer, "Missing closing '}' for " "indexed block."); } m_indexed_block_map->addIndexedBlockBuffer(name, std::move(ibb)); } } // end of namespace mae } // end of namespace schrodinger maeparser-1.3.1/MaeParser.hpp000066400000000000000000000226031436653602000160730ustar00rootroot00000000000000#ifndef MAE_READER_HPP_ #define MAE_READER_HPP_ // Visual Studio versions prior to 2015 don't support noexcept #if defined(_MSC_FULL_VER) && _MSC_FULL_VER < 190023026 #define NOEXCEPT #else #define NOEXCEPT noexcept #endif #include #include #include #include #include #include #include #include "Buffer.hpp" #include "MaeBlock.hpp" #include "MaeParserConfig.hpp" #define MAEPARSER_EXCEPTION_BUFFER_SIZE 256 namespace schrodinger { namespace mae { /** * Parse (and throw away) a comment of the form '# comment #'. */ void comment(Buffer& buffer); /** * Parse (and throw away) zero or more characters of whitespace including \t, * \r, \n and ' ', along with any embedded comments. */ EXPORT_MAEPARSER void whitespace(Buffer& buffer); /** * Parse a triple colon or raise an exception. */ void triple_colon(Buffer& buffer); /** * Parse the specific character requested. Return true if successful, false if * not. */ bool character(char c, Buffer& buffer); /** * Parse the specific character requested. Return true if successful, false if * not. Update any save points if buffer reload is required. */ bool character(char c, Buffer& buffer, char*& save); /** * Parse a full (b|i|r|s)__ property key. * * Return NULL if a starting character of ':' is found (the beginning of the * ':::' property name terminator). * * Raise a read_exception in any other situation. */ std::shared_ptr property_key(Buffer& buffer); /** * Read through the opening '{' of a named or unnamed outer block. * * Set the name of the block in the provided argument; an empty string * if unnamed. If EOF is reached, return false, otherwise return true. */ EXPORT_MAEPARSER std::string outer_block_beginning(Buffer& buffer); template T parse_value(Buffer& buffer); class EXPORT_MAEPARSER read_exception : public std::exception { private: char m_msg[MAEPARSER_EXCEPTION_BUFFER_SIZE]; void format(size_t line_number, size_t column, const char* msg); public: read_exception(const Buffer& buffer, const char* msg) { format(buffer.line_number, buffer.getColumn(), msg); } read_exception(size_t line_number, size_t column, const char* msg) { format(line_number, column, msg); } const char* what() const NOEXCEPT override { return m_msg; } }; /** * A pure virtual base class for parsers. Allows us to store these in * collections, so we can do things like easily invoke different parsers on * each element while parsing a row of values. */ class EXPORT_MAEPARSER Parser { public: virtual void parse(Buffer& buffer) = 0; virtual ~Parser() = default; }; class EXPORT_MAEPARSER IndexedBlockParser { std::vector m_property_names; public: virtual ~IndexedBlockParser() = default; virtual void parse(const std::string& name, size_t size, Buffer& buffer) = 0; virtual std::shared_ptr getIndexedBlockMap() = 0; }; class EXPORT_MAEPARSER IndexedBlockBuffer { private: std::vector m_property_names; std::string m_name; TokenBufferList m_tokens_list; size_t m_rows; public: IndexedBlockBuffer(std::string name, size_t rows) : m_property_names(), m_name(std::move(name)), m_rows(rows) { } virtual ~IndexedBlockBuffer() = default; void addPropertyName(std::string&& name) { m_property_names.push_back(name); } /** * Parse the indexed block values, store them in a linked list of buffers. */ virtual void parse(Buffer& buffer); /** * Parse a value. */ void value(Buffer& buffer); void getData(size_t ix, const char** data, size_t* const len) const { m_tokens_list.getData(ix, data, len); } std::string getName() const { return m_name; } size_t size() const { return m_rows; } IndexedBlock* getIndexedBlock(); }; class EXPORT_MAEPARSER BufferedIndexedBlockParser : public IndexedBlockParser { private: std::shared_ptr m_indexed_block_map; public: BufferedIndexedBlockParser(); std::shared_ptr getIndexedBlockMap() override; void parse(const std::string& name, size_t size, Buffer& buffer) override; }; class EXPORT_MAEPARSER DirectIndexedBlockParser : public IndexedBlockParser { std::shared_ptr m_indexed_block_map; public: void parse(const std::string& name, size_t size, Buffer& buffer) override; std::shared_ptr getIndexedBlockMap() override; }; class EXPORT_MAEPARSER IndexedValueParser : public Parser { public: virtual void addToIndexedBlock(IndexedBlock* block) = 0; }; template class IndexedValueCollector : public IndexedValueParser { public: std::string m_name; std::vector m_values; boost::dynamic_bitset<>* m_is_null; public: explicit IndexedValueCollector(std::string name, size_t size) : m_name(std::move(name)), m_values(), m_is_null(nullptr) { m_values.reserve(size); m_is_null = nullptr; } ~IndexedValueCollector() override { if (m_is_null) { delete m_is_null; } } void parse(Buffer& buffer) override { if (buffer.current >= buffer.end) { if (!buffer.load()) { throw read_exception(buffer, "Unexpected EOF."); } } if (*buffer.current == '<') { char* save = buffer.current; ++buffer.current; if (buffer.current >= buffer.end) { if (!buffer.load(save)) { throw read_exception(buffer, "Unexpected EOF."); } } // TODO: not sure, but I assume that unquoted strings like // are allowed as values. Ugh. This requires saving the // starting point of '<' in case we need to back up. if (*buffer.current != '>') { // Back up and parse as a normal value. --buffer.current; } else { ++buffer.current; if (m_is_null == nullptr) { m_is_null = new boost::dynamic_bitset<>(m_values.capacity()); } m_is_null->set(m_values.size()); m_values.push_back(T()); return; } } m_values.push_back(parse_value(buffer)); } void addToIndexedBlock(IndexedBlock* block) override { auto ptr = std::shared_ptr>( new IndexedProperty(m_values, m_is_null)); block->setProperty(m_name, ptr); m_is_null = nullptr; } }; class EXPORT_MAEPARSER MaeParser { protected: Buffer m_buffer; std::shared_ptr m_stream; virtual IndexedBlockParser* getIndexedBlockParser() { return new BufferedIndexedBlockParser(); } public: explicit MaeParser(const std::shared_ptr& stream, size_t buffer_size = BufferLoader::DEFAULT_SIZE) : m_buffer(*stream, buffer_size), m_stream(stream) { m_buffer.load(); } explicit MaeParser(FILE* file, size_t buffer_size = BufferLoader::DEFAULT_SIZE) : m_buffer(file, buffer_size) { if (file == nullptr) { std::string msg("Bad file argument"); if (errno != 0) { msg += ": "; msg += strerror(errno); } else { msg += "."; } throw std::runtime_error(msg); } m_buffer.load(); } // TODO: finish big three (four) virtual ~MaeParser() = default; std::shared_ptr blockBody(const std::string& name); IndexedBlock* indexedBlock(const std::string& name, size_t size); // TODO: private IndexedBlockBuffer* indexedBlockBuffer(const std::string& name, size_t size); std::shared_ptr outerBlock(); /** * Read a block name or a closing '}'. The argument 'indexed' is set to * a positive integer value indicating the number of rows, or zero if * the block is not indexed. * * Return the block name or NULL if the closing '}' was found. */ std::string blockBeginning(int* indexed); std::shared_ptr property(); /** * Read a list of properties, ending at the name/value separator. * Populate the provided std::vector of std::string shared pointers. */ void properties(std::vector>* property_names); /** * Read (and throw away) any whitespace. */ void whitespace() { schrodinger::mae::whitespace(m_buffer); } }; class EXPORT_MAEPARSER DirectMaeParser : public MaeParser { public: explicit DirectMaeParser(const std::shared_ptr& stream, size_t buffer_size = BufferLoader::DEFAULT_SIZE) : MaeParser(stream, buffer_size) { } explicit DirectMaeParser(FILE* file, size_t buffer_size = BufferLoader::DEFAULT_SIZE) : MaeParser(file, buffer_size) { } private: IndexedBlockParser* getIndexedBlockParser() override { return new DirectIndexedBlockParser(); } }; } // end namespace mae } // end namespace schrodinger #endif maeparser-1.3.1/MaeParserConfig.hpp000066400000000000000000000005311436653602000172150ustar00rootroot00000000000000#pragma once #ifndef STATIC_MAEPARSER #ifdef WIN32 #ifdef IN_MAEPARSER #define EXPORT_MAEPARSER __declspec(dllexport) #else #define EXPORT_MAEPARSER __declspec(dllimport) #endif // IN_MAEPARSER #else #define EXPORT_MAEPARSER __attribute__((visibility("default"))) #endif // WIN32 #else #define EXPORT_MAEPARSER #endif // STATIC_MAEPARSER maeparser-1.3.1/README.md000066400000000000000000000072111436653602000147600ustar00rootroot00000000000000# maeparser [![Azure_Build_Status](https://dev.azure.com/patlorton/maeparser/_apis/build/status/schrodinger.maeparser?branchName=master)](https://dev.azure.com/patlorton/maeparser/_build/latest?definitionId=1&branchName=master) [![Build_Status](https://ci.appveyor.com/api/projects/status/github/schrodinger/maeparser?branch=master&svg=true)](https://ci.appveyor.com/project/torcolvin/maeparser) maeparser is a parser for [Schrodinger](https://www.schrodinger.com/) Maestro files. Structure files (.mae,.maegz,.mae.gz) can contain multiple structures delimited by "f_m_ct". See [MaeConstants.hpp](MaeConstants.hpp) for standard block and property names. To read a structure, ```C++ #include "Reader.hpp" ... FILE* f = fopen("test.mae", "r"); schrodinger::mae::Reader r(f); std::shared_ptr b; while ((b = r.next(schrodinger::mae::CT_BLOCK)) != nullptr) { // Parse structure } fclose(f); ``` See also test/UsageDemo.cpp, which reads an example structure and stores it in a dummy Molecule class. Background ========== Why do we have a recursive descent parser for mae files instead of using a parser generator like ANTLR or lex/yacc? The main reasons are that the mae format language is 1) pretty simple, 2) unlikely to change significantly, and 3) not a context free grammar. In addition, speed of parsing these files is important. In what way is the current version of the language not a CFG? Special tokens like block opener `{` and key/value separator `:::` can also be string values because the quotes on string values are not required. This results in complication and pain in attempts to define a grammar. Why this format? ================= There are many molecule formats out there, and the significant strength of this one is that it exactly fits the use case of Schrödinger's physics-based modeling. As the primary (and only lossless) Schrödinger output format, any package that wishes to implement lossless data extraction of Maestro output needs to interact directly with this format. This is not an intentional limitation, but is due to the nature of chemical storage formats: it's extremely hard to get a format that's both 1) Flexible enough to hold any type of data and 2) Not so flexible that each user has to implement their own rules. The Maestro format avoids this paradox by having the exact flexibility that Schrödinger's physics based backends require, without additional flexibility that other use cases might demand. In supporting Schrödinger's backend suite, maeparser is able to handle output from: * Molecular Dynamics applications, such as Desmond and FEP+ * Ligand-Protein Docking applications, such as Glide * Homology Modeling and folding applications, such as Prime * Ligand-based search applications, such as Phase and Phase Shape * Quantum Mechanics applications, such as Jaguar * Protein-Protein Docking applications * Many other backends used in both Life and Material Sciences Installation ============ Command line installation on a Unix-like operating system follows a typical configure, build, and test procedure. Configuration is via [CMake](https://cmake.org/). Here is an example command sequence: ```bash git clone git@github.com:schrodinger/maeparser.git maeparser # Set up the build configuration cd maeparser mkdir build cd build export CC=gcc cmake --verbose .. # Build it make # Run the custom testing ctest ``` Defining CC ensures that the specified compiler is used in the build (in the example, it will be the first instance of `gcc` in one's PATH), and the `--verbose` argumentenables viewing the gory details of compiling and linking that will be necessary for debugging or reporting issues if the build fails. maeparser-1.3.1/Reader.cpp000066400000000000000000000037351436653602000154160ustar00rootroot00000000000000#include "Reader.hpp" #include #include #include #include #include #include #include #include using boost::algorithm::ends_with; using boost::iostreams::file_source; using boost::iostreams::filtering_istream; namespace schrodinger { namespace mae { Reader::Reader(FILE* file, size_t buffer_size) { m_mae_parser.reset(new MaeParser(file, buffer_size)); } Reader::Reader(const std::shared_ptr& stream, size_t buffer_size) { m_mae_parser.reset(new MaeParser(stream, buffer_size)); } Reader::Reader(const std::string& fname, size_t buffer_size) { const auto ios_mode = std::ios_base::in | std::ios_base::binary; std::shared_ptr stream; if (ends_with(fname, ".maegz") || ends_with(fname, ".mae.gz")) { auto* gzip_stream = new filtering_istream(); gzip_stream->push(boost::iostreams::gzip_decompressor()); gzip_stream->push(file_source(fname, ios_mode)); stream.reset(static_cast(gzip_stream)); } else { auto* file_stream = new std::ifstream(fname, ios_mode); stream.reset(static_cast(file_stream)); } if (stream->fail()) { std::stringstream ss; ss << "Failed to open file \"" << fname << "\" for reading operation."; throw std::runtime_error(ss.str()); } m_mae_parser.reset(new MaeParser(stream, buffer_size)); } Reader::Reader(std::shared_ptr mae_parser) : m_mae_parser(std::move(mae_parser)) { } std::shared_ptr Reader::next(const std::string& outer_block_name) { std::shared_ptr block; do { m_mae_parser->whitespace(); block = m_mae_parser->outerBlock(); } while (block != nullptr && block->getName() != outer_block_name); return block; } } // namespace mae } // namespace schrodinger maeparser-1.3.1/Reader.hpp000066400000000000000000000017631436653602000154220ustar00rootroot00000000000000#pragma once #include #include #include #include "Buffer.hpp" #include "MaeBlock.hpp" #include "MaeParser.hpp" #include "MaeParserConfig.hpp" namespace schrodinger { namespace mae { class EXPORT_MAEPARSER Reader { private: std::shared_ptr m_mae_parser; public: Reader() = delete; Reader(FILE* file, size_t buffer_size = BufferLoader::DEFAULT_SIZE); Reader(const std::shared_ptr& stream, size_t buffer_size = BufferLoader::DEFAULT_SIZE); Reader(const std::string& fname, size_t buffer_size = BufferLoader::DEFAULT_SIZE); // Should be made private if we conclude there's no need for the // DirectParser. The only current purpose of allowing construction from a // MaeParser is to allow direct/buffered behavior difference. Reader(std::shared_ptr mae_parser); std::shared_ptr next(const std::string& outer_block_name); }; } // namespace mae } // namespace schrodinger maeparser-1.3.1/Writer.cpp000066400000000000000000000031601436653602000154600ustar00rootroot00000000000000#include "Writer.hpp" #include #include #include #include #include #include #include #include #include "MaeBlock.hpp" using namespace std; using boost::algorithm::ends_with; using boost::iostreams::file_sink; using boost::iostreams::filtering_ostream; namespace schrodinger { namespace mae { Writer::Writer(std::shared_ptr stream) : m_out(std::move(stream)) { write_opening_block(); } Writer::Writer(const std::string& fname) { const auto ios_mode = std::ios_base::out | std::ios_base::binary; if (ends_with(fname, ".maegz") || ends_with(fname, ".mae.gz")) { auto* gzip_stream = new filtering_ostream(); gzip_stream->push(boost::iostreams::gzip_compressor()); gzip_stream->push(file_sink(fname, ios_mode)); m_out.reset(static_cast(gzip_stream)); } else { auto* file_stream = new ofstream(fname, ios_mode); m_out.reset(static_cast(file_stream)); } if (m_out->fail()) { std::stringstream ss; ss << "Failed to open file \"" << fname << "\" for writing operation."; throw std::runtime_error(ss.str()); } write_opening_block(); } void Writer::write(const std::shared_ptr& block) { block->write(*m_out); } void Writer::write_opening_block() { shared_ptr b = make_shared(""); b->setStringProperty("s_m_m2io_version", "2.0.0"); write(b); } } // namespace mae } // namespace schrodinger maeparser-1.3.1/Writer.hpp000066400000000000000000000010061436653602000154620ustar00rootroot00000000000000#pragma once #include #include #include #include "MaeParserConfig.hpp" namespace schrodinger { namespace mae { class Block; class EXPORT_MAEPARSER Writer { private: std::shared_ptr m_out = nullptr; void write_opening_block(); public: Writer() = delete; explicit Writer(const std::string& fname); Writer(std::shared_ptr stream); void write(const std::shared_ptr& block); }; } // namespace mae } // namespace schrodinger maeparser-1.3.1/appveyor.yml000066400000000000000000000020131436653602000160640ustar00rootroot00000000000000environment: P: "%APPVEYOR_BUILD_FOLDER%\\install" BOOST_ROOT_PATH: C:\Libraries\boost BOOST_LIBRARY_PATH: "%BOOST_ROOT_PATH%\\lib64-msvc-12.0" image: Visual Studio 2013 platform: x64 configuration: - Release - Debug branches: only: - master - devel install: # by default, all script lines are interpreted as batch build: project: "%APPVEYOR_BUILD_FOLDER%\\build\\ALL_BUILD.vcxproj" parallel: true before_build: - mkdir %APPVEYOR_BUILD_FOLDER%\build - cd %APPVEYOR_BUILD_FOLDER%\build - cmake .. ^ -G "Visual Studio 12 Win64" ^ -DMAEPARSER_RIGOROUS_BUILD=ON ^ -DCMAKE_BUILD_TYPE=%CONFIGURATION% ^ -DBOOST_ROOT=%BOOST_ROOT_PATH% ^ -DCMAKE_INSTALL_PREFIX=%P% test_script: - set PATH=%BOOST_LIBRARY_PATH%;%PATH% - cd %APPVEYOR_BUILD_FOLDER%\build # For whatever reason, boost_iostreams depends on libbz2.dll - copy %BOOST_LIBRARY_PATH%\boost_bzip2-vc120-mt-1_56.dll %APPVEYOR_BUILD_FOLDER%\build\test\%CONFIGURATION%\libbz2.dll - ctest -V -C %CONFIGURATION% maeparser-1.3.1/azure-pipelines.yml000066400000000000000000000036751436653602000173520ustar00rootroot00000000000000trigger: - master - dev/* jobs: - job: Ubuntu_20_04_x64 timeoutInMinutes: 90 pool: vmImage: ubuntu-20.04 variables: compiler: gxx_linux-64 boost_version: 1.67.0 number_of_cores: nproc python_name: python37 shared_lib: ON steps: - template: .azure-pipelines/linux_build.yml - job: Ubuntu_20_04_x64_static timeoutInMinutes: 90 pool: vmImage: ubuntu-20.04 variables: compiler: gxx_linux-64 boost_version: 1.67.0 number_of_cores: nproc python_name: python37 shared_lib: OFF steps: - template: .azure-pipelines/linux_build.yml - job: macOS_10_15_x64 timeoutInMinutes: 90 pool: vmImage: macos-10.15 variables: compiler: clangxx_osx-64 boost_version: 1.67.0 number_of_cores: sysctl -n hw.ncpu python_name: python37 target_platform: 10.9 shared_lib: ON steps: - template: .azure-pipelines/mac_build.yml - job: macOS_10_15_x64_static timeoutInMinutes: 90 pool: vmImage: macos-10.15 variables: compiler: clangxx_osx-64 boost_version: 1.67.0 number_of_cores: sysctl -n hw.ncpu python_name: python37 target_platform: 10.9 shared_lib: OFF steps: - template: .azure-pipelines/mac_build.yml - job: Windows_VS2019_x64 timeoutInMinutes: 90 pool: vmImage: windows-2019 variables: compiler: vs2019_win-64 boost_version: 1.67.0 number_of_cores: "%NUMBER_OF_PROCESSORS%" python_name: python37 shared_lib: ON steps: - template: .azure-pipelines/vs_build.yml - job: Windows_VS2019_x64_static timeoutInMinutes: 90 pool: vmImage: windows-2019 variables: compiler: vs2019_win-64 boost_version: 1.67.0 number_of_cores: "%NUMBER_OF_PROCESSORS%" python_name: python37 shared_lib: OFF steps: - template: .azure-pipelines/vs_build.yml maeparser-1.3.1/package-lock.json000066400000000000000000000004001436653602000167060ustar00rootroot00000000000000{ "name": "maeparser", "author": "schrodinger", "repository": "github:schrodinger/maeparser", "license": "MIT", "version": "1.2.4", "dependencies": { "cmake": "Kitware/CMake#v3.5.0", "boost": "boostorg/boost", } } maeparser-1.3.1/test/000077500000000000000000000000001436653602000144575ustar00rootroot00000000000000maeparser-1.3.1/test/BufferTest.cpp000066400000000000000000000107571436653602000172460ustar00rootroot00000000000000#include #include #include #include #include #include #include "Buffer.hpp" using namespace schrodinger; BOOST_AUTO_TEST_SUITE(BufferSuite) BOOST_AUTO_TEST_CASE(NewlineColumn0) { std::string s("123456"); std::stringstream ss(s); Buffer b(ss, s.size()); BOOST_REQUIRE(b.load()); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 0), 1u); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 2), 3u); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 5), 6u); } BOOST_AUTO_TEST_CASE(NewlineColumn1) { std::string s("\n123456"); std::stringstream ss(s); Buffer b(ss, s.size()); BOOST_REQUIRE(b.load()); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 0), 1u); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 1), 1u); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 3), 3u); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin + 6), 6u); } BOOST_AUTO_TEST_CASE(NewlineColumnEmptyBuffer) { std::string s; std::stringstream ss(s); Buffer b(ss, s.size()); BOOST_REQUIRE(!b.load()); BOOST_REQUIRE_EQUAL(b.getColumn(b.begin), 1u); } BOOST_AUTO_TEST_CASE(Loading) { std::stringstream ss("123456"); Buffer b(ss, 6); int i = 0; while (b.current < b.end || b.load()) { ++b.current; ++i; } BOOST_REQUIRE_EQUAL(i, 6); } BOOST_AUTO_TEST_CASE(GetBuffer) { std::stringstream ss("123456123456"); int buffer_len = 6; Buffer b(ss, buffer_len); for (int i = 0; i < buffer_len; i++) { if (!b.load()) { BOOST_FAIL("Unexpected end of \"file\"."); } ++b.current; } if (!b.load()) { BOOST_FAIL("Unexpected end of \"file\"."); } BOOST_REQUIRE_EQUAL(*b.current, '1'); } BOOST_AUTO_TEST_CASE(Newline0) { // Test newline as the first character of a new buffer load. std::stringstream ss("123456\n"); int buffer_len = 6; Buffer b(ss, buffer_len); for (int i = 0; i < buffer_len; i++) { if (!b.load()) { BOOST_FAIL("Unexpected end of \"file\"."); } ++b.current; } BOOST_REQUIRE_EQUAL(b.getColumn(), 7u); if (!b.load()) { BOOST_FAIL("Unexpected end of \"file\"."); } BOOST_REQUIRE_EQUAL(*b.current, '\n'); ++b.current; BOOST_REQUIRE_EQUAL(b.getColumn(), 1u); } BOOST_AUTO_TEST_CASE(GetPosition0) { // Test expected column numbers for repeated buffer loads. std::stringstream ss("0123456\n12345x7\n\n123y5\n"); // 0......1.......2........3. // 12345678 12345678 1 123456 const unsigned int buffer_len = 7; const unsigned int stream_len = 23; Buffer b(ss, buffer_len); const int required_reads = 4; unsigned int column[stream_len] = {1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1, 1, 2, 3, 4, 5, 6}; int stream_ix = 0; int load_count = 0; while (b.current < b.end || (b.load() && ++load_count)) { BOOST_REQUIRE_EQUAL(b.getColumn(), column[stream_ix]); ++b.current; ++stream_ix; } BOOST_REQUIRE_EQUAL(load_count, required_reads); } BOOST_AUTO_TEST_CASE(GetPosition1) { // Test expected column numbers for repeated buffer loads with // the smallest possible buffer size. std::stringstream ss("0123456"); int buffer_len = 1; Buffer b(ss, buffer_len); const int required_reads = 7; unsigned int column[required_reads] = {1, 2, 3, 4, 5, 6, 7}; int load_count = 0; while (b.current < b.end || (b.load() && ++load_count)) { BOOST_REQUIRE_EQUAL(b.getColumn(), column[load_count - 1]); ++b.current; } BOOST_REQUIRE_EQUAL(load_count, required_reads); } BOOST_AUTO_TEST_CASE(NewlineCase0) { // Test the corner case of a single newline in the entire stream. std::stringstream ss("\n"); Buffer b(ss, 128); BOOST_REQUIRE_EQUAL(b.getColumn(), 1u); while (b.current < b.end || b.load()) { ++b.current; } BOOST_REQUIRE_EQUAL(b.getColumn(), 1u); } BOOST_AUTO_TEST_CASE(NewlineCase1) { // Test column position around newlines. This case has a newline as the // first char in a new buffer. std::stringstream ss("123456\n890\n1"); Buffer b(ss, 10); const int stream_len = 12; unsigned int column[stream_len] = {1, 2, 3, 4, 5, 6, 7, 1, 2, 3, 4, 1}; int ix = 0; while (b.current < b.end || b.load()) { BOOST_REQUIRE_EQUAL(b.getColumn(), column[ix]); ++b.current; ++ix; } } BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/CMakeLists.txt000066400000000000000000000022221436653602000172150ustar00rootroot00000000000000include_directories(..) find_package(Boost COMPONENTS filesystem iostreams unit_test_framework REQUIRED) add_executable(unittest MainTestSuite.cpp BufferTest.cpp MaeBlockTest.cpp MaeParserTest.cpp ReaderTest.cpp WriterTest.cpp UsageDemo.cpp) if(MAEPARSER_BUILD_SHARED_LIBS) target_compile_definitions(unittest PRIVATE "BOOST_ALL_DYN_LINK") else(MAEPARSER_BUILD_SHARED_LIBS) target_compile_definitions(unittest PRIVATE "STATIC_MAEPARSER") endif(MAEPARSER_BUILD_SHARED_LIBS) get_filename_component(TEST_SAMPLES_PATH ${CMAKE_CURRENT_SOURCE_DIR} ABSOLUTE) target_compile_definitions(unittest PRIVATE "TEST_SAMPLES_PATH=\"${TEST_SAMPLES_PATH}\"") target_link_libraries(unittest maeparser ${Boost_LIBRARIES}) add_test(NAME unittest COMMAND ${CMAKE_CURRENT_BINARY_DIR}/unittest WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) if(MSVC AND MAEPARSER_BUILD_SHARED_LIBS) add_custom_command(TARGET unittest POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different "${PROJECT_BINARY_DIR}/\$\(Configuration\)/maeparser.dll" "${CMAKE_CURRENT_BINARY_DIR}/\$\(Configuration\)/maeparser.dll") endif(MSVC AND MAEPARSER_BUILD_SHARED_LIBS) maeparser-1.3.1/test/MaeBlockTest.cpp000066400000000000000000000234161436653602000175060ustar00rootroot00000000000000#include #include #include #include #include "MaeBlock.hpp" #include "MaeConstants.hpp" using namespace schrodinger; BOOST_AUTO_TEST_SUITE(MaeBlockSuite) BOOST_AUTO_TEST_CASE(maeBlock) { double tolerance = std::numeric_limits::epsilon(); mae::Block b("dummy"); b.setRealProperty("a", 1.0); BOOST_REQUIRE(b.hasRealProperty("a")); BOOST_REQUIRE(!b.hasRealProperty("b")); BOOST_REQUIRE_CLOSE(b.getRealProperty("a"), 1.0, tolerance); BOOST_REQUIRE_THROW(b.getRealProperty("b"), std::out_of_range); b.setIntProperty("a", 3); BOOST_REQUIRE(b.hasIntProperty("a")); BOOST_REQUIRE(!b.hasIntProperty("b")); BOOST_REQUIRE_EQUAL(b.getIntProperty("a"), 3); BOOST_REQUIRE_THROW(b.getIntProperty("b"), std::out_of_range); b.setBoolProperty("a", true); BOOST_REQUIRE(b.hasBoolProperty("a")); BOOST_REQUIRE(!b.hasBoolProperty("b")); BOOST_REQUIRE(b.getBoolProperty("a")); BOOST_REQUIRE_THROW(b.getBoolProperty("b"), std::out_of_range); const std::vector strings = {"Regular", "Spaced String"}; for (const auto& value : strings) { b.setStringProperty("a", value); BOOST_REQUIRE(b.hasStringProperty("a")); BOOST_REQUIRE(!b.hasStringProperty("b")); BOOST_REQUIRE_EQUAL(b.getStringProperty("a"), value); BOOST_REQUIRE_THROW(b.getStringProperty("b"), std::out_of_range); } // All the properties we set have the same name, but different types const std::string property_name("a"); auto bool_props = b.getProperties(); BOOST_CHECK_EQUAL(bool_props.size(), 1u); BOOST_CHECK_EQUAL(bool_props.begin()->first, property_name); auto int_props = b.getProperties(); BOOST_CHECK_EQUAL(int_props.size(), 1u); BOOST_CHECK_EQUAL(int_props.begin()->first, property_name); auto double_props = b.getProperties(); BOOST_CHECK_EQUAL(double_props.size(), 1u); BOOST_CHECK_EQUAL(double_props.begin()->first, property_name); auto string_props = b.getProperties(); BOOST_CHECK_EQUAL(string_props.size(), 1u); BOOST_CHECK_EQUAL(string_props.begin()->first, property_name); } BOOST_AUTO_TEST_CASE(maeIndexedRealProperty) { double tolerance = std::numeric_limits::epsilon(); { auto dv = std::make_shared>(); dv->push_back(1.0); dv->push_back(2.0); dv->push_back(3.0); mae::IndexedRealProperty irp(*dv); BOOST_REQUIRE_CLOSE(irp[0], 1.0, tolerance); BOOST_REQUIRE_CLOSE(irp[1], 2.0, tolerance); BOOST_REQUIRE_CLOSE(irp[2], 3.0, tolerance); } { std::vector dv; dv.push_back(1.0); dv.push_back(2.0); dv.push_back(3.0); mae::IndexedRealProperty irp(dv); BOOST_REQUIRE_CLOSE(irp[0], 1.0, tolerance); BOOST_REQUIRE_CLOSE(irp[1], 2.0, tolerance); BOOST_REQUIRE_CLOSE(irp[2], 3.0, tolerance); } { auto dv = std::make_shared>(); boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); dv->push_back(1.0); dv->push_back(0.0); dv->push_back(3.0); mae::IndexedRealProperty irp(*dv, bs); BOOST_REQUIRE(irp.isDefined(0)); BOOST_REQUIRE_CLOSE(irp[0], 1.0, tolerance); BOOST_REQUIRE(!irp.isDefined(1)); BOOST_REQUIRE_THROW(irp[1], std::runtime_error); BOOST_REQUIRE(irp.isDefined(2)); BOOST_REQUIRE_CLOSE(irp[2], 3.0, tolerance); } { std::vector dv; boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); dv.push_back(1.0); dv.push_back(0.0); dv.push_back(3.0); mae::IndexedRealProperty irp(dv, bs); BOOST_REQUIRE(irp.isDefined(0)); BOOST_REQUIRE_CLOSE(irp[0], 1.0, tolerance); BOOST_REQUIRE(!irp.isDefined(1)); BOOST_REQUIRE_THROW(irp[1], std::runtime_error); BOOST_REQUIRE(irp.isDefined(2)); BOOST_REQUIRE_CLOSE(irp[2], 3.0, tolerance); } } BOOST_AUTO_TEST_CASE(maeIndexedBlock) { using namespace mae; double tolerance = std::numeric_limits::epsilon(); { std::vector dv; boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); dv.push_back(1.0); dv.push_back(0.0); dv.push_back(3.0); IndexedBlock ib("m_atom"); BOOST_REQUIRE(!ib.hasRealProperty("r_m_float")); auto irps = std::make_shared(dv, bs); ib.setRealProperty("r_m_float", irps); BOOST_REQUIRE(ib.hasRealProperty("r_m_float")); auto irpg = ib.getRealProperty("r_m_float"); IndexedRealProperty& irp = *irpg; BOOST_REQUIRE(irp.isDefined(0)); BOOST_REQUIRE_CLOSE(irp[0], 1.0, tolerance); BOOST_REQUIRE_CLOSE(irp.at(0, 999.0), 1.0, tolerance); BOOST_REQUIRE(!irp.isDefined(1)); BOOST_REQUIRE_THROW(irp[1], std::runtime_error); BOOST_REQUIRE_CLOSE(irp.at(1, 999.0), 999.0, tolerance); BOOST_REQUIRE(irp.isDefined(2)); BOOST_REQUIRE_CLOSE(irp[2], 3.0, tolerance); BOOST_REQUIRE_CLOSE(irp.at(2, 999.0), 3.0, tolerance); auto no_prop = ib.getRealProperty("r_m_nonexistant"); BOOST_REQUIRE(no_prop.get() == nullptr); } } BOOST_AUTO_TEST_CASE(maeIndexedBlockBool) { using namespace mae; { std::vector dv; boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); dv.push_back(true); dv.push_back(false); dv.push_back(true); IndexedBlock ib("m_atom"); BOOST_REQUIRE(!ib.hasBoolProperty("b_m_bool")); auto ibps = std::make_shared(dv, bs); ib.setBoolProperty("b_m_bool", ibps); BOOST_REQUIRE(ib.hasBoolProperty("b_m_bool")); auto ibpg = ib.getBoolProperty("b_m_bool"); IndexedBoolProperty& ibp = *ibpg; BOOST_REQUIRE(ibp.isDefined(0)); BOOST_REQUIRE_EQUAL(ibp[0], static_cast(true)); BOOST_REQUIRE(!ibp.isDefined(1)); BOOST_REQUIRE_THROW(ibp[1], std::runtime_error); BOOST_REQUIRE(ibp.isDefined(2)); BOOST_REQUIRE_EQUAL(ibp[2], static_cast(true)); } } BOOST_AUTO_TEST_CASE(maeIndexedBlockString) { using namespace mae; { std::vector dv; boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); dv.push_back("Hi with space"); dv.push_back("ignore me"); dv.push_back("Bye"); IndexedBlock ib("m_atom"); BOOST_REQUIRE(!ib.hasStringProperty("s_m_string")); auto isps = std::make_shared(dv, bs); ib.setStringProperty("s_m_string", isps); BOOST_REQUIRE(ib.hasStringProperty("s_m_string")); auto ispg = ib.getStringProperty("s_m_string"); IndexedStringProperty& isp = *ispg; BOOST_REQUIRE(isp.isDefined(0)); BOOST_REQUIRE_EQUAL(isp[0], "Hi with space"); BOOST_REQUIRE(!isp.isDefined(1)); BOOST_REQUIRE_THROW(isp[1], std::runtime_error); BOOST_REQUIRE(isp.isDefined(2)); BOOST_REQUIRE_EQUAL(isp[2], "Bye"); } } std::shared_ptr getExampleIndexedBlock() { using namespace mae; auto ib = std::make_shared("m_atom"); // Set up a bool property std::vector dv = {true, false, true}; boost::dynamic_bitset<>* bs = new boost::dynamic_bitset<>(3); bs->set(1); auto ibps = std::make_shared(dv, bs); ib->setBoolProperty("b_m_bool", ibps); // Set up a real property std::vector rv = {0.1, 42}; boost::dynamic_bitset<>* rbs = new boost::dynamic_bitset<>(3); rbs->set(2); auto irps = std::make_shared(rv, rbs); ib->setRealProperty("r_m_reals", irps); return ib; } BOOST_AUTO_TEST_CASE(toStringProperties) { const std::string rval = R"(dummy { b_m_bool r_m_real i_m_int s_m_string ::: 0 1.000000 42 "mae\"parser" m_atom[3] { # First column is Index # b_m_bool r_m_reals ::: 1 1 0.100000 2 <> 42.000000 3 1 <> ::: } } )"; mae::Block b("dummy"); b.setRealProperty("r_m_real", 1.0); b.setBoolProperty("b_m_bool", false); b.setIntProperty("i_m_int", 42); b.setStringProperty("s_m_string", "mae\"parser"); auto ib = getExampleIndexedBlock(); auto ibm = std::make_shared(); ibm->addIndexedBlock(ib->getName(), ib); b.setIndexedBlockMap(ibm); BOOST_REQUIRE_EQUAL(b.toString(), rval); // All the properties we set have the same name, but different types auto bool_props = b.getProperties(); BOOST_CHECK_EQUAL(bool_props.size(), 1u); BOOST_CHECK_EQUAL(bool_props.begin()->first, std::string("b_m_bool")); auto int_props = b.getProperties(); BOOST_CHECK_EQUAL(int_props.size(), 1u); BOOST_CHECK_EQUAL(int_props.begin()->first, std::string("i_m_int")); auto double_props = b.getProperties(); BOOST_CHECK_EQUAL(double_props.size(), 1u); BOOST_CHECK_EQUAL(double_props.begin()->first, std::string("r_m_real")); auto string_props = b.getProperties(); BOOST_CHECK_EQUAL(string_props.size(), 1u); BOOST_CHECK_EQUAL(string_props.begin()->first, std::string("s_m_string")); } BOOST_AUTO_TEST_CASE(toStringIndexedProperties) { using namespace mae; const std::string rval = R"(m_atom[3] { # First column is Index # b_m_bool r_m_reals ::: 1 1 0.100000 2 <> 42.000000 3 1 <> ::: } )"; auto ib = getExampleIndexedBlock(); BOOST_REQUIRE_EQUAL(ib->toString(), rval); } BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/MaeParserTest.cpp000066400000000000000000000745321436653602000177150ustar00rootroot00000000000000#include #include #include #include "Buffer.hpp" #include "MaeBlock.hpp" #include "MaeConstants.hpp" #include "MaeParser.hpp" using namespace schrodinger; using namespace schrodinger::mae; using std::shared_ptr; using boost::trim_copy; static std::string get_string(IndexedBlockBuffer& indexed_block_buffer, size_t index) { const char* ptr = nullptr; size_t len = 0; indexed_block_buffer.getData(index, &ptr, &len); return std::string(ptr, len); } BOOST_AUTO_TEST_SUITE(MaeParserSuite) BOOST_AUTO_TEST_CASE(OuterBlockBeginning) { { std::stringstream ss("{"); Buffer b(ss); b.load(); std::string name = outer_block_beginning(b); BOOST_REQUIRE_EQUAL(name, ""); } { std::stringstream ss("f_m_ct {"); Buffer b(ss); b.load(); std::string name = outer_block_beginning(b); BOOST_REQUIRE_EQUAL(name, CT_BLOCK); } { std::stringstream ss("f_m_ct{"); Buffer b(ss); b.load(); std::string name = outer_block_beginning(b); BOOST_REQUIRE_EQUAL(name, CT_BLOCK); } { // TODO: Check that this is actually the desired behavior; I'm not // sure that underscores are allowed in block names. std::stringstream ss("f_m_ct_block{"); Buffer b(ss); b.load(); std::string name = outer_block_beginning(b); BOOST_REQUIRE_EQUAL(name, "f_m_ct_block"); } } BOOST_AUTO_TEST_CASE(OuterBlockBeginErrors) { { std::stringstream ss("b_m_ct {"); Buffer b(ss); b.load(); try { std::string name = outer_block_beginning(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 1: " "Bad format for outer block name; " "must be (f|p)__."); } } { std::stringstream ss("f_m {"); Buffer b(ss); b.load(); try { std::string name = outer_block_beginning(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 4: " "Bad format for outer block name; " "must be (f|p)__."); } } { std::stringstream ss("full_m_ct {"); Buffer b(ss); b.load(); try { std::string name = outer_block_beginning(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 2: " "Bad format for outer block name; " "must be (f|p)__."); } } { std::stringstream ss("f_m_ct b_m_foo"); Buffer b(ss); b.load(); try { std::string name = outer_block_beginning(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 10: " "Missing '{' for outer block."); } } } BOOST_AUTO_TEST_CASE(BlockBeginning) { { auto ss = std::make_shared("m_something {"); int indexed = 0; MaeParser mp(ss); std::string name = mp.blockBeginning(&indexed); BOOST_REQUIRE_EQUAL(name, "m_something"); BOOST_REQUIRE_EQUAL(indexed, 0); } { auto ss = std::make_shared("mmmm_block{"); int indexed = 0; MaeParser mp(ss); std::string name = mp.blockBeginning(&indexed); BOOST_REQUIRE_EQUAL(name, "mmmm_block"); BOOST_REQUIRE_EQUAL(indexed, 0); } { auto ss = std::make_shared("m_whatev[23]{"); int indexed = 0; MaeParser mp(ss); std::string name = mp.blockBeginning(&indexed); BOOST_REQUIRE_EQUAL(name, "m_whatev"); BOOST_REQUIRE_EQUAL(indexed, 23); } } BOOST_AUTO_TEST_CASE(BlockBeginningErrors) { { try { auto ss = std::make_shared(""); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL( trim_copy(std::string(e.what())), "Line 1, column 1: " "Bad format for block name; must be _."); } } { try { auto ss = std::make_shared("m_block[integer]"); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 9: " "Unexpected character."); } } { try { auto ss = std::make_shared("m_block[33 "); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 13: " "Bad block index; missing ']'."); } } { try { auto ss = std::make_shared("m_block[33] s_m_foo"); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 14: " "Missing '{' for block."); } } { try { auto ss = std::make_shared("'bad_block"); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL( trim_copy(std::string(e.what())), "Line 1, column 1: " "Bad format for block name; must be _."); } } { try { auto ss = std::make_shared("mmmm_ "); int indexed = 0; MaeParser mp(ss); mp.blockBeginning(&indexed); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL( trim_copy(std::string(e.what())), "Line 1, column 6: " "Bad format for block name; must be _."); } } } BOOST_AUTO_TEST_CASE(BlockBody) { { auto ss = std::make_shared(" b_m_foo b_m_bar ::: 1 0 }"); MaeParser mp(ss); auto bl = mp.blockBody(CT_BLOCK); BOOST_REQUIRE(bl->getBoolProperty("b_m_foo")); BOOST_REQUIRE(!bl->getBoolProperty("b_m_bar")); } { auto ss = std::make_shared( " b_m_foo b_m_bar s_m_foo r_m_foo i_m_foo ::: " " 1 0 svalue 3.1415 22 }"); MaeParser mp(ss); auto bl = mp.blockBody(CT_BLOCK); BOOST_REQUIRE(bl->getBoolProperty("b_m_foo")); BOOST_REQUIRE(!bl->getBoolProperty("b_m_bar")); BOOST_REQUIRE_EQUAL(bl->getStringProperty("s_m_foo"), "svalue"); double tolerance = std::numeric_limits::epsilon(); BOOST_REQUIRE_CLOSE(bl->getRealProperty("r_m_foo"), 3.1415, tolerance); BOOST_REQUIRE_EQUAL(bl->getIntProperty("i_m_foo"), 22); } } BOOST_AUTO_TEST_CASE(BlockBodyErrors) { { auto ss = std::make_shared( " b_m_foo\n s_m_foo\n r_m_foo\n i_m_foo\n :::\n" " 1\n svalue\n 3.1415\n 22\n "); MaeParser mp(ss); try { mp.blockBody(CT_BLOCK); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 10, column 2: " "Missing '}' for block."); } } } BOOST_AUTO_TEST_CASE(Property) { { auto ss = std::make_shared("b_m_foo "); MaeParser mp(ss); auto p = mp.property(); BOOST_REQUIRE(*p == "b_m_foo"); } { auto ss = std::make_shared("r_m_bar "); MaeParser mp(ss); auto p = mp.property(); BOOST_REQUIRE(*p == "r_m_bar"); } { auto ss = std::make_shared("b_st_1_2_3_4_R_5 "); MaeParser mp(ss); auto p = mp.property(); BOOST_REQUIRE(*p == "b_st_1_2_3_4_R_5"); } { auto ss = std::make_shared("s_author_name "); MaeParser mp(ss); auto p = mp.property(); BOOST_REQUIRE(*p == "s_author_name"); } } BOOST_AUTO_TEST_CASE(PropertyValueSeparator) { { auto ss = std::make_shared(":::"); MaeParser mp(ss); auto p = mp.property(); BOOST_REQUIRE(p == nullptr); } } BOOST_AUTO_TEST_CASE(PropertyList) { { std::vector properties; auto ss = std::make_shared("b_m_foo s_j_bar :::"); // Buffer size of 14 was chosen to reproduce a bug during development; // a buffer boundary in the name part of the key. MaeParser mp(ss, 14u); while (true) { auto p = mp.property(); mp.whitespace(); if (p == nullptr) { break; } properties.push_back(*p); } BOOST_REQUIRE_EQUAL(properties.size(), 2u); BOOST_REQUIRE_EQUAL(properties[0], "b_m_foo"); BOOST_REQUIRE_EQUAL(properties[1], "s_j_bar"); } { std::vector properties; auto ss = std::make_shared("b_m_foo s_j_ :::"); MaeParser mp(ss); try { while (true) { auto p = mp.property(); mp.whitespace(); if (p == nullptr) { break; } properties.push_back(*p); } BOOST_FAIL("Exception expected."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 13: " "Bad format for property; " "must be (b|i|r|s)__."); } } } BOOST_AUTO_TEST_CASE(PropertyErrors) { { try { auto ss = std::make_shared("bo_m_foo "); MaeParser mp(ss); mp.property(); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 2: " "Bad format for property; " "must be (b|i|r|s)__."); } } { try { auto ss = std::make_shared("x_m_foo "); MaeParser mp(ss); mp.property(); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 1: " "Bad format for property; " "must be (b|i|r|s)__."); } } { try { auto ss = std::make_shared("s_m_"); MaeParser mp(ss); mp.property(); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 5: " "Bad format for property; " "must be (b|i|r|s)__."); } } { try { auto ss = std::make_shared("s_m_ "); MaeParser mp(ss); mp.property(); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 5: " "Bad format for property; " "must be (b|i|r|s)__."); } } } BOOST_AUTO_TEST_CASE(Integer) { { std::stringstream ss("1234"); Buffer b(ss); BOOST_REQUIRE_EQUAL(parse_value(b), 1234); } { std::stringstream ss("-1234"); Buffer b(ss); BOOST_REQUIRE_EQUAL(parse_value(b), -1234); } { std::stringstream ss("2147483647"); Buffer b(ss); BOOST_REQUIRE_EQUAL(parse_value(b), 2147483647); } { // There is a bug in some VS editions that raises warning C4146 // when assigning -2147483648 to an int in code.. const int reference = std::numeric_limits::min(); std::stringstream ss; ss << reference; Buffer b(ss); BOOST_REQUIRE_EQUAL(parse_value(b), reference); } } BOOST_AUTO_TEST_CASE(IntegerErrors) { { std::stringstream ss("12-34"); Buffer b(ss); try { parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 3: Unexpected '-'."); } } { std::stringstream ss("-12-34"); Buffer b(ss); try { parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 4: Unexpected '-'."); } } { std::stringstream ss("\n\n]"); Buffer b(ss); whitespace(b); try { parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 3, column 1: Missing integer."); } } { std::stringstream ss("\n\n123*]"); Buffer b(ss); whitespace(b); try { parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 3, column 4: Unexpected character."); } } } BOOST_AUTO_TEST_CASE(Real) { double tolerance = std::numeric_limits::epsilon(); { std::stringstream ss("-2.3 "); Buffer b(ss); BOOST_REQUIRE_CLOSE(parse_value(b), -2.3, tolerance); } { std::stringstream ss("-24.3"); Buffer b(ss); BOOST_REQUIRE_CLOSE(parse_value(b), -24.3, tolerance); } { std::stringstream ss("-2.3e10 "); Buffer b(ss); BOOST_REQUIRE_CLOSE(parse_value(b), -2.3e10, tolerance); } { std::stringstream ss("-2.3E10"); Buffer b(ss); BOOST_REQUIRE_CLOSE(parse_value(b), -2.3e10, tolerance); } } BOOST_AUTO_TEST_CASE(RealErrors) { { try { std::stringstream ss(""); Buffer b(ss); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 1: " "Missing real."); } } { try { std::stringstream ss("-2.3{"); Buffer b(ss); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 5: " "Unexpected character in real number."); } } { try { std::stringstream ss("\n -2.3. "); Buffer b(ss); whitespace(b); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 2, column 6: " "Bad real number."); } } { // Different versions of boost give different results due to // boost::spirit const std::string valid_error1("Line 2, column 2: Bad real number."); const std::string valid_error2("Line 2, column 4: Bad real number."); try { std::stringstream ss("\n -2EE3. "); Buffer b(ss); whitespace(b); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_MESSAGE( trim_copy(std::string(e.what())) == valid_error1 || trim_copy(std::string(e.what())) == valid_error2, "Expected " << valid_error1 << " or " << valid_error2 << " but got " << e.what()); } } } BOOST_AUTO_TEST_CASE(String) { { std::stringstream ss("-2.3E10 "); Buffer b(ss); whitespace(b); std::string s = parse_value(b); BOOST_REQUIRE_EQUAL(s, "-2.3E10"); } { std::stringstream ss(R"(Q\ Z)"); Buffer b(ss); whitespace(b); std::string s = parse_value(b); BOOST_REQUIRE_EQUAL(s, "Q\\"); } { std::stringstream ss(R"("Q\ Z")"); Buffer b(ss); whitespace(b); std::string s = parse_value(b); BOOST_REQUIRE_EQUAL(s, R"(Q Z)"); } { std::stringstream ss(R"("a b c d e")"); Buffer b(ss); whitespace(b); std::string s = parse_value(b); BOOST_REQUIRE_EQUAL(s, "a b c d e"); } { std::stringstream ss(" abcd" "ef"); Buffer b(ss, 5); whitespace(b); std::string s = parse_value(b); BOOST_REQUIRE_EQUAL(s, "abcdef"); } } BOOST_AUTO_TEST_CASE(StringErrors) { { try { std::stringstream ss(R"("a b c d e)"); Buffer b(ss); whitespace(b); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 11: " "Unterminated quoted string at EOF."); } } } BOOST_AUTO_TEST_CASE(Boolean) { { std::stringstream ss(" 1"); Buffer b(ss); whitespace(b); BOOST_REQUIRE_EQUAL(parse_value(b), static_cast(true)); } { std::stringstream ss("0 "); Buffer b(ss); whitespace(b); BOOST_REQUIRE_EQUAL(parse_value(b), static_cast(false)); } } BOOST_AUTO_TEST_CASE(BooleanErrors) { { try { std::stringstream ss("\n\n\na"); Buffer b(ss); whitespace(b); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 4, column 1: " "Unexpected character for boolean value."); } } { try { std::stringstream ss("\t\n\n11"); Buffer b(ss); whitespace(b); parse_value(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 3, column 2: " "Unexpected character for boolean value."); } } } BOOST_AUTO_TEST_SUITE_END() BOOST_AUTO_TEST_SUITE(IndexedBlockBufferSuite) BOOST_AUTO_TEST_CASE(TestIndexedValueCollectorInt) { std::stringstream ss(" 1 " " <> " " 2"); IndexedValueCollector ivc("i_m_foo", 3); Buffer b(ss, 5); BOOST_REQUIRE(b.load()); whitespace(b); ivc.parse(b); whitespace(b); ivc.parse(b); whitespace(b); ivc.parse(b); BOOST_REQUIRE_EQUAL(ivc.m_values[0], 1); BOOST_REQUIRE_EQUAL(ivc.m_values[1], 0); BOOST_REQUIRE_EQUAL(ivc.m_values[2], 2); } BOOST_AUTO_TEST_CASE(TestIndexedValueCollectorDouble) { std::stringstream ss(" 1 " " <> " " 2.1"); IndexedValueCollector ivc("r_m_foo", 3); Buffer b(ss, 5); BOOST_REQUIRE(b.load()); whitespace(b); ivc.parse(b); whitespace(b); ivc.parse(b); whitespace(b); ivc.parse(b); BOOST_REQUIRE_EQUAL(ivc.m_values[0], 1.0); BOOST_REQUIRE_EQUAL(ivc.m_values[1], 0.0); BOOST_REQUIRE_EQUAL(ivc.m_values[2], 2.1); } BOOST_AUTO_TEST_CASE(TestIndexedValueCollectorFunctors) { std::stringstream ss(" 1 " " <> " " <> " " 2.1 " " 3 " " 0.1 "); Buffer b(ss, 5); IndexedValueCollector real_ivc("r_m_foo", 3); IndexedValueCollector int_ivc("i_m_foo", 3); std::vector parsers; parsers.push_back(&int_ivc); parsers.push_back(&real_ivc); BOOST_REQUIRE(b.load()); for (int i = 0; i < 3; ++i) { for (auto parser : parsers) { whitespace(b); parser->parse(b); } } BOOST_CHECK_EQUAL(int_ivc.m_values[0], 1); BOOST_CHECK_EQUAL(int_ivc.m_values[1], 0); BOOST_CHECK_EQUAL(int_ivc.m_values[2], 3); BOOST_CHECK_EQUAL(real_ivc.m_values[0], 0.0); BOOST_CHECK_EQUAL(real_ivc.m_values[1], 2.1); BOOST_CHECK_EQUAL(real_ivc.m_values[2], 0.1); } // Split string. BOOST_AUTO_TEST_CASE(Test0) { std::stringstream ss(" 1 " " abc " " ghij" "k "); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghijk")); } // First string starts at column 1. // Blank starting buffer. // Blank buffer between strings. // Blank buffer after all strings. BOOST_AUTO_TEST_CASE(Test1) { std::stringstream ss(" 1 " " " "abc " " " " ghij" "k " " "); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghijk")); } // Two strings that each fill a buffer and are separated by a blank buffer. BOOST_AUTO_TEST_CASE(Test2) { std::stringstream ss(" 1 " "abcde" " " "fghij"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("f"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abcde")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("fghij")); } // Second string starts at buffer start, ends on next (and final) buffer end. BOOST_AUTO_TEST_CASE(Test3) { std::stringstream ss(" 1 " " abc " "fghij" "klmno"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("f"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("fghijklmno")); } // Second string starts at buffer start, ends on next (and final) buffer end. BOOST_AUTO_TEST_CASE(Test3p1) { std::stringstream ss(" 1 " " abc " "fghij" "klmno" "pqrst"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("f"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("fghijklmnopqrst")); } // First string ends at buffer end. // Second string spans an entire buffer in the middle. BOOST_AUTO_TEST_CASE(Test4) { std::stringstream ss(" 1 " " abc" " ghij" "klmno" "p "); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghijklmnop")); } // Three separate strings with no split strings. BOOST_AUTO_TEST_CASE(Test5) { std::stringstream ss(" 1 " " " " abc" " ghi " "jkl"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); ibb.addPropertyName("j"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghi")); z = get_string(ibb, 3); BOOST_REQUIRE_EQUAL(z, std::string("jkl")); } // Multi-buffer string that ends on a buffer end. // Whitespace following last string doesn't end on buffer end. BOOST_AUTO_TEST_CASE(Test6) { std::stringstream ss(" 1 " " a c" "fghij" " lmno" "p x "); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("c"); ibb.addPropertyName("x"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("a")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("cfghij")); z = get_string(ibb, 3); BOOST_REQUIRE_EQUAL(z, std::string("lmnop")); } // Multiple strings in one buffer. // Second buffer that's ignored. BOOST_AUTO_TEST_CASE(Test7) { std::stringstream ss(" 1 " " abc ghi " "xxxx "); Buffer b(ss, 10); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("abc")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghi")); } // Unexpected EOF due to missing row number. BOOST_AUTO_TEST_CASE(Test8) { std::stringstream ss(" abc ghi "); Buffer b(ss, 10); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("a"); ibb.addPropertyName("g"); try { ibb.parse(b); BOOST_FAIL("Expected an exception."); } catch (read_exception& e) { BOOST_REQUIRE_EQUAL(trim_copy(std::string(e.what())), "Line 1, column 11: " "Unexpected EOF in indexed block values."); } } BOOST_AUTO_TEST_CASE(TestQuotedStrings1) { std::stringstream ss(R"( 1 "bc " ghijk )"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("b"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("\"bc \"")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghijk")); } BOOST_AUTO_TEST_CASE(TestQuotedStrings2) { std::stringstream ss(R"( 1 "bc \"" ghijk )"); Buffer b(ss, 5); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("b"); ibb.addPropertyName("g"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("\"bc \\\"\"")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("ghijk")); } BOOST_AUTO_TEST_CASE(TestOneCharValues) { std::stringstream ss(" 1 1 2 3 40 "); Buffer b(ss, 128); IndexedBlockBuffer ibb("m_test", 1); ibb.addPropertyName("1"); ibb.addPropertyName("2"); ibb.addPropertyName("3"); ibb.addPropertyName("40"); ibb.parse(b); std::string z; z = get_string(ibb, 1); BOOST_REQUIRE_EQUAL(z, std::string("1")); z = get_string(ibb, 2); BOOST_REQUIRE_EQUAL(z, std::string("2")); z = get_string(ibb, 3); BOOST_REQUIRE_EQUAL(z, std::string("3")); z = get_string(ibb, 4); BOOST_REQUIRE_EQUAL(z, std::string("40")); } BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/MainTestSuite.cpp000066400000000000000000000001171436653602000177200ustar00rootroot00000000000000#define BOOST_TEST_MODULE mae_reader_test #include maeparser-1.3.1/test/ReaderTest.cpp000066400000000000000000000227641436653602000172400ustar00rootroot00000000000000#include #include #include #include #include #include "MaeBlock.hpp" #include "MaeConstants.hpp" #include "Reader.hpp" #include "TestCommon.hpp" using namespace schrodinger::mae; using std::shared_ptr; const boost::filesystem::path test_samples_path(TEST_SAMPLES_PATH); const std::string uncompressed_sample = (test_samples_path / "test.mae").string(); const std::string subblock_sample = (test_samples_path / "subblock_sample.mae").string(); BOOST_AUTO_TEST_SUITE(ReaderSuite) BOOST_AUTO_TEST_CASE(Reader0) { auto ss = std::make_shared(); *ss << "\n" << "{" "\n" << " s_m_m2io_version" "\n" << " :::" "\n" << " 1.1.0 " "\n" << "}" "\n"; Reader r(ss); auto b = r.next(""); BOOST_REQUIRE(b); BOOST_REQUIRE_EQUAL(b->getStringProperty(MAE_FORMAT_VERSION), "1.1.0"); } BOOST_AUTO_TEST_CASE(NamedBlock0) { auto ss = std::make_shared(); *ss << "\n" << "\n" << "f_m_ct {" "\n" << " s_m_prop" "\n" << " :::" "\n" << " 1.1.0 " "\n" << "}" "\n"; Reader r(ss); auto b = r.next(CT_BLOCK); BOOST_REQUIRE(b); BOOST_REQUIRE_EQUAL(b->getStringProperty("s_m_prop"), "1.1.0"); } BOOST_AUTO_TEST_CASE(NamedBlock1) { auto ss = std::make_shared(); *ss << "{" "\n" << " s_m_m2io_version" "\n" << " :::" "\n" << " 1.1.0 " "\n" << "}" "\n" << "\n" << "f_m_ct {" "\n" << " s_m_prop" "\n" << " :::" "\n" << " 1.1.0 " "\n" << "}" "\n"; Reader r(ss); auto b = r.next(CT_BLOCK); BOOST_REQUIRE(b); BOOST_REQUIRE_EQUAL(b->getStringProperty("s_m_prop"), "1.1.0"); b = r.next(CT_BLOCK); BOOST_REQUIRE(b == nullptr); } BOOST_AUTO_TEST_CASE(NestedBlock) { auto ss = std::make_shared(); *ss << "{" "\n" << " s_m_m2io_version" "\n" << " :::" "\n" << " 1.1.0 " "\n" << "}" "\n" << "\n" << "f_m_ct {" "\n" << " s_m_prop" "\n" << " :::" "\n" << " 1.1.0 " "\n" << " m_nested {" "\n" << " s_m_prop" "\n" << " :::" "\n" << " 1.1.0 " "\n" << " }" "\n" << "}" "\n"; Reader r(ss); auto b = r.next(CT_BLOCK); BOOST_REQUIRE(b); BOOST_REQUIRE_EQUAL(b->getStringProperty("s_m_prop"), "1.1.0"); BOOST_REQUIRE(b->hasBlock("m_nested")); BOOST_REQUIRE_EQUAL(b->getBlock("m_nested")->getStringProperty("s_m_prop"), "1.1.0"); } BOOST_AUTO_TEST_CASE(NestedIndexedBlock) { auto ss = std::make_shared(); *ss << "{" "\n" // 1 << " s_m_m2io_version" "\n" // 2 << " :::" "\n" // 3 << " 1.1.0 " "\n" // 4 << "}" "\n" // 5 << "\n" // 6 << "f_m_ct {" "\n" // 7 << " s_m_prop" "\n" // 8 << " :::" "\n" // 9 << " 1.1.0 " "\n" // 10 << " m_nested[2] {" "\n" // 11 << " s_m_prop" "\n" // 12 << " :::" "\n" // 13 << " 1 1.1.0 " "\n" // 14 << " 2 1.1.0 " "\n" // 15 << " :::" "\n" // 16 << " }" "\n" // 17 << " m_bond[2] {" "\n" // 18 << " s_m_prop" "\n" // 19 << " :::" "\n" // 20 << " 1 1.1.0 " "\n" // 21 << " 2 1.1.0 " "\n" // 22 << " :::" "\n" // 23 << " }" "\n" // 24 << " m_dependencies {" "\n" // 25 << " s_m_prop" "\n" // 26 << " :::" "\n" // 27 << " 1.1.0 " "\n" // 28 << " }" "\n" // 29 << "}" "\n"; // 30 Reader r(ss); auto b = r.next(CT_BLOCK); BOOST_REQUIRE(b); auto ibn = b->getIndexedBlock("m_nested"); auto prop = ibn->getStringProperty("s_m_prop"); BOOST_REQUIRE_EQUAL((*prop)[0], "1.1.0"); BOOST_REQUIRE_EQUAL((*prop)[1], "1.1.0"); } BOOST_AUTO_TEST_CASE(BufferedReader) { auto ss = std::make_shared(uncompressed_sample); Reader r(ss); size_t count = 0; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { auto iba = b->getIndexedBlock(ATOM_BLOCK); auto ibb = b->getIndexedBlock(BOND_BLOCK); count++; } BOOST_REQUIRE_EQUAL(count, 3u); } BOOST_AUTO_TEST_CASE(BufferedFileReader) { FILE* f = fopen(uncompressed_sample.c_str(), "r"); Reader r(f); size_t count = 0; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { auto iba = b->getIndexedBlock(ATOM_BLOCK); auto ibb = b->getIndexedBlock(BOND_BLOCK); count++; } fclose(f); BOOST_REQUIRE_EQUAL(count, 3u); } BOOST_AUTO_TEST_CASE(TextReader) { auto ss = std::make_shared(uncompressed_sample); Reader r(ss); size_t count = 0; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { count++; } BOOST_REQUIRE_EQUAL(count, 3u); } BOOST_AUTO_TEST_CASE(TextFileReader) { FILE* f = fopen(uncompressed_sample.c_str(), "r"); Reader r(f); size_t count = 0; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { count++; } fclose(f); BOOST_REQUIRE_EQUAL(count, 3u); } BOOST_AUTO_TEST_CASE(DirectReader) { FILE* f = fopen(uncompressed_sample.c_str(), "r"); auto mae_parser = std::make_shared(f); Reader r(mae_parser); size_t count = 0; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { auto iba = b->getIndexedBlock(ATOM_BLOCK); auto ibb = b->getIndexedBlock(BOND_BLOCK); count++; } fclose(f); BOOST_REQUIRE_EQUAL(count, 3u); } BOOST_AUTO_TEST_CASE(QuotedStringTest) { FILE* f = fopen(uncompressed_sample.c_str(), "r"); Reader r(f); auto b = r.next("f_m_ct"); auto title = b->getStringProperty("s_m_title"); BOOST_REQUIRE_EQUAL(title, R"(Title with p \ " space)"); auto atom_block = b->getIndexedBlock("m_atom"); auto pdb_res = atom_block->getStringProperty("s_m_pdb_residue_name"); BOOST_REQUIRE_EQUAL(pdb_res->at(0), "UNK "); auto atom_names = atom_block->getStringProperty("s_m_atom_name"); BOOST_REQUIRE_EQUAL(atom_names->at(0), R"(Does p " \this work)"); fclose(f); } BOOST_AUTO_TEST_CASE(TestReadNonExistingFile) { // This file should not exist! CheckExceptionMsg check_msg( "Failed to open file \"non_existing_file.mae\" for reading operation"); BOOST_CHECK_EXCEPTION(Reader r("non_existing_file.mae"), std::runtime_error, check_msg); } void write_block_names(const Block& block, int tabs, std::vector>& res) { for (auto& subblock_name : block.getBlockNames()) { res.push_back({subblock_name, tabs}); write_block_names(*block.getBlock(subblock_name), tabs + 1, res); } for (auto& indexed_subblock_name : block.getIndexedBlockNames()) { res.push_back({indexed_subblock_name, tabs}); } } BOOST_AUTO_TEST_CASE(TestGetSubBlockNames) { auto ss = std::make_shared(subblock_sample); Reader r(ss); std::shared_ptr b = r.next(CT_BLOCK); /* This is the tree structure of the non atom or bond subblocks for this CT block: m_test_block m_nested_block m_test_nested_indexed_block m_test_block m_test_repeated_block m_test_indexed_block */ std::vector> expected_subblocks = { {"m_test_block", 0}, {"m_nested_block", 1}, {"m_test_nested_indexed_block", 2}, {"m_test_block", 1}, {"m_test_repeated_block", 1}, {"m_test_indexed_block", 1}, {schrodinger::mae::ATOM_BLOCK, 0}, {schrodinger::mae::BOND_BLOCK, 0}, }; std::vector> actual_subblocks; write_block_names(*b, 0, actual_subblocks); BOOST_REQUIRE(actual_subblocks.size() == expected_subblocks.size()); for (unsigned int i = 0; i < actual_subblocks.size(); ++i) { auto actual = actual_subblocks[i]; auto expected = expected_subblocks[i]; BOOST_CHECK_EQUAL(actual.first, expected.first); BOOST_CHECK_EQUAL(actual.second, expected.second); } } BOOST_AUTO_TEST_CASE(TestParsingPropertyNameWithColon) { auto ss = std::make_shared(); *ss << R"( f_m_ct { s_m_prop:name::with:::many::::colons ::: 1.1.0 } )"; Reader r(ss); auto b = r.next(CT_BLOCK); BOOST_REQUIRE(b); BOOST_CHECK_EQUAL(b->getStringProperty("s_m_prop:name::with:::many::::colons"), "1.1.0"); } BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/TestCommon.hpp000066400000000000000000000014161436653602000172620ustar00rootroot00000000000000#pragma once #include #include #include template class CheckExceptionMsg { public: CheckExceptionMsg(std::string msg) : m_expected(std::move(msg)), m_match(false) { } ~CheckExceptionMsg() { // This message should be printer after BOOST_REQUIRE_EXCEPTION's std::stringstream ss; ss << '\"' << m_expected << "\" not found in \"" << m_message << '\"'; BOOST_CHECK_MESSAGE(m_match, ss.str()); } bool operator()(const T& exc) { m_message = exc.what(); m_match = m_message.find(m_expected) != std::string::npos; return m_match; } private: const std::string m_expected; std::string m_message; bool m_match; };maeparser-1.3.1/test/UsageDemo.cpp000066400000000000000000000113451436653602000170400ustar00rootroot00000000000000/// // Demonstrate reading a schrodinger/Maestro formatted file and gleaning the // bonding information, coordinates, and atomic number. // // Note that Maestro format uses 1-based indices when referring to indexed // data in blocks. (for example, the atom numbers in bond blocks are 1 indexed.) // // Maestro "structures" may contain multiple non-bonded molecules in a // coherent environment. For instance, both a ligand and a receptor may exist // in a single f_m_ct block. // #include #include #include #include #include #include #include #include "MaeConstants.hpp" #include "Reader.hpp" #include #include using namespace schrodinger::mae; // These classes are not intended for production use. The are merely intended // to illustrate where data is stored in the "block" data structures. class Bond { public: Bond(int atom0, int atom1, int bond_order) : atom0(atom0), atom1(atom1), bond_order(bond_order) { } const int atom0; const int atom1; const int bond_order; }; class Structure { public: std::string title; std::vector atomic_numbers; std::vector> coordinates; std::vector bonds; // A "property" that some atoms have (others may not have this property) std::unordered_map demo_property; }; const boost::filesystem::path test_samples_path(TEST_SAMPLES_PATH); const std::string compressed_sample = (test_samples_path / "test2.maegz").string(); BOOST_AUTO_TEST_SUITE(DemoSuite) // Reads all atom and bond information from test.mae, which is a standard // mae formatted file. Only accesses properties that are gauranteed to // exist in every f_m_ct block. BOOST_AUTO_TEST_CASE(maeBlock) { schrodinger::mae::Reader r(compressed_sample); std::vector> structures; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { auto st = std::make_shared(); st->title = b->getStringProperty(CT_TITLE); // Atom data is in the m_atom indexed block { const auto atom_data = b->getIndexedBlock(ATOM_BLOCK); // All atoms are gauranteed to have these three field names: const auto atomic_numbers = atom_data->getIntProperty(ATOM_ATOMIC_NUM); const auto xs = atom_data->getRealProperty(ATOM_X_COORD); const auto ys = atom_data->getRealProperty(ATOM_Y_COORD); const auto zs = atom_data->getRealProperty(ATOM_Z_COORD); const auto size = atomic_numbers->size(); BOOST_REQUIRE_EQUAL(size, xs->size()); BOOST_REQUIRE_EQUAL(size, ys->size()); BOOST_REQUIRE_EQUAL(size, zs->size()); // atomic numbers, and x, y, and z coordinates for (size_t i = 0; i < size; ++i) { st->atomic_numbers.push_back(atomic_numbers->at(i)); st->coordinates.push_back({{xs->at(i), ys->at(i), zs->at(i)}}); } // Other properties could fail, because not all properties have // values for all atoms. The last atom of the first structure does // not have the "i_m_template_index" property. const auto template_indices = atom_data->getIntProperty("i_m_template_index"); for (size_t i = 0; i < size; ++i) { if (template_indices->isDefined(i)) { st->demo_property[i] = template_indices->at(i); } } } // Bond data is in the m_bond indexed block { const auto bond_data = b->getIndexedBlock(BOND_BLOCK); // All bonds are gauranteed to have these three field names: auto bond_atom_1s = bond_data->getIntProperty(BOND_ATOM_1); auto bond_atom_2s = bond_data->getIntProperty(BOND_ATOM_2); auto orders = bond_data->getIntProperty(BOND_ORDER); const auto size = bond_atom_1s->size(); for (size_t i = 0; i < size; ++i) { // Atom indices in the bond data structure are 1 indexed! const auto bond_atom_1 = bond_atom_1s->at(i) - 1; const auto bond_atom_2 = bond_atom_2s->at(i) - 1; const auto order = orders->at(i); // Only one direction of the bond is recorded in the file st->bonds.emplace_back(bond_atom_1, bond_atom_2, order); st->bonds.emplace_back(bond_atom_2, bond_atom_1, order); } } structures.push_back(st); } // Check that all three f_m_ct blocks were read. BOOST_CHECK_EQUAL(structures.size(), 3u); } BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/WriterTest.cpp000066400000000000000000000065211436653602000173030ustar00rootroot00000000000000#include #include #include #include #include "MaeBlock.hpp" #include "MaeConstants.hpp" #include "Reader.hpp" #include "TestCommon.hpp" #include "Writer.hpp" #include #include using namespace schrodinger::mae; using std::shared_ptr; // We should make sure these do not exist before the tests starts, // this will prevent any chances of finding the results of an older test. const std::vector generated_files = {"test_write.mae", "test_write.maegz"}; const boost::filesystem::path test_samples_path(TEST_SAMPLES_PATH); const std::string uncompressed_sample = (test_samples_path / "test.mae").string(); class WriterGlobalFixture { public: WriterGlobalFixture() { for (const auto& file : generated_files) { boost::filesystem::path fpath(file); if (boost::filesystem::exists(fpath)) { boost::filesystem::remove(fpath); } } } }; BOOST_GLOBAL_FIXTURE(WriterGlobalFixture); BOOST_AUTO_TEST_SUITE(WriterSuite) BOOST_AUTO_TEST_CASE(Writer0) { Reader r(uncompressed_sample); auto w = std::make_shared("test_write.mae"); std::vector> input; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { input.push_back(b); w->write(b); } w.reset(); // Explicitly reset to flush file IO in writer Reader output_r("test_write.mae"); int input_num = 0; while ((b = output_r.next(CT_BLOCK)) != nullptr) { BOOST_CHECK(*b == *(input[input_num++])); } } BOOST_AUTO_TEST_CASE(Writer1) { Reader r(uncompressed_sample); auto w = std::make_shared("test_write.maegz"); std::vector> input; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { input.push_back(b); w->write(b); } w.reset(); // Explicitly reset to flush file IO in writer Reader output_r("test_write.maegz"); int input_num = 0; while ((b = output_r.next(CT_BLOCK)) != nullptr) { BOOST_CHECK(*b == *(input[input_num++])); } } BOOST_AUTO_TEST_CASE(TestWriteNonAccessiblePath) { // This path should not exist/be accesible! CheckExceptionMsg check_msg( "Failed to open file \"/non/accessible/path/file.mae\" for writing " "operation"); BOOST_CHECK_EXCEPTION(Writer w("/non/accessible/path/file.mae"), std::runtime_error, check_msg); } /* // UNCOMMENT BLOCK TO TEST PERFORMANCE OF LIGAND WRITING BOOST_AUTO_TEST_CASE(PerfTest) { Reader r(uncompressed_sample); auto w = std::make_shared("test_write.maegz"); std::vector > input; std::shared_ptr b; while ((b = r.next(CT_BLOCK)) != nullptr) { input.push_back(b); } int total_write = 0; auto start = std::clock(); for(int i=0; i<10000; i++) { for(const auto& b : input) { w->write(b); total_write++; } } auto duration = ( std::clock() - start ) / (double) CLOCKS_PER_SEC; std::cout<<"Runtime: "<< duration <<'\n' << " Structures: " << total_write; std::cout<<"Speed: "<< total_write/duration <<'\n'; } */ BOOST_AUTO_TEST_SUITE_END() maeparser-1.3.1/test/subblock_sample.mae000066400000000000000000000027741436653602000203220ustar00rootroot00000000000000{ s_m_m2io_version ::: 2.0.0 } f_m_ct { s_m_title s_m_entry_id s_m_entry_name i_m_ct_format ::: water 2 water.1 2 m_atom[3] { # First column is atom index # i_m_mmod_type r_m_x_coord r_m_y_coord r_m_z_coord i_m_residue_number i_m_color s_m_pdb_residue_name s_m_grow_name i_m_atomic_number s_m_color_rgb s_m_atom_name i_m_minimize_atom_index ::: 1 16 -1.523268 2.566995 0.086033 1 70 " " " c1" 8 FF2F2F O1 1 2 42 -2.329434 1.994698 -0.064311 1 21 " " " c2" 1 FFFFFF H2 2 3 42 -0.697113 2.013954 -0.021723 1 21 " " " n2" 1 FFFFFF H3 3 ::: } m_bond[2] { # First column is bond index # i_m_from i_m_to i_m_order i_m_from_rep i_m_to_rep ::: 1 1 2 1 1 1 2 1 3 1 1 1 ::: } m_test_block { r_test_real i_test_int s_test_string ::: 0.5 1 hello m_test_block { i_test_i2 ::: 11 } m_test_indexed_block[2] { i_test_ia b_test_bb ::: 1 101 1 2 102 0 ::: } m_test_repeated_block { i_test_irep ::: 1001 } m_test_repeated_block { i_test_irep ::: 1002 } m_nested_block { i_test_none ::: 1003 m_test_nested_indexed_block[2] { s_test_s1 r_test_r2 ::: 1 abc 1.5 2 def 0.7 ::: } } } } maeparser-1.3.1/test/test.mae000066400000000000000000000243171436653602000161310ustar00rootroot00000000000000{ s_m_m2io_version ::: 2.0.0 } f_m_ct { s_m_title s_m_entry_name r_mmod_Potential_Energy-OPLS-2005 b_mmod_Minimization_Converged-OPLS-2005 r_mmod_RMS_Derivative-OPLS-2005 i_mmod_Times_Found-OPLS-2005 r_mmod_Relative_Potential_Energy-OPLS-2005 b_mmod_Chiralities_Consistent-OPLS-2005 i_mmod_Conformation-OPLS-2005 i_mmod_Serial_Number-OPLS-2005 i_sd_Mol\_ID s_sd_Formula r_sd_MolWeight i_sd_SchIID s_sd_Source i_sd_Source\_ID s_sd_Chemical_Name r_sd_pKa\_1 i_sd_Temp\_1 s_sd_Assessment\_1 s_sd_Identifier\_1 s_user_Data_Set r_sd_pKa\_1a r_sd_pKa\_1b r_sd_pKa\_1c s_m_entry_id ::: "Title with \p \\ \" space" ligprep-out.1 11.5775423049927 1 0.000241500383708626 1 0 1 1 1 1 CH2O2 46.02538 1 SB79 2002 "Methanoic Acid" 3.749 25 Rel. carboxylicAcid_aliphatic Acids 3.737 3.739 3.772 1 m_depend[8] { # First column is dependency index # i_m_depend_dependency s_m_depend_property ::: 1 20 r_mmod_Potential_Energy-OPLS-2005 2 20 b_mmod_Minimization_Converged-OPLS-2005 3 20 r_mmod_RMS_Derivative-OPLS-2005 4 20 i_mmod_Times_Found-OPLS-2005 5 20 r_mmod_Relative_Potential_Energy-OPLS-2005 6 20 b_mmod_Chiralities_Consistent-OPLS-2005 7 20 i_mmod_Conformation-OPLS-2005 8 10 i_mmod_Serial_Number-OPLS-2005 ::: } m_atom[5] { # First column is atom index # i_m_mmod_type r_m_x_coord r_m_y_coord r_m_z_coord i_m_residue_number s_m_insertion_code s_m_mmod_res s_m_chain_name i_m_color r_m_charge1 r_m_charge2 s_m_pdb_residue_name s_m_pdb_atom_name s_m_grow_name i_m_atomic_number i_m_formal_charge i_m_representation i_m_visibility s_m_atom_name i_m_template_index ::: 1 2 1.322943 0.665651 0.033620 900 " " X " " 2 0.52000 0.52000 "UNK " " " " " 6 0 0 1 "Does \p \" \\this work" 0 2 15 2.383264 0.045183 0.036722 900 " " X " " 70 -0.44000 -0.44000 "UNK " " " " " 8 0 0 1 "" 0 3 16 0.088569 0.086898 0.011423 900 " " X " " 70 -0.53000 -0.53000 "UNK " " " " " 8 0 0 1 "" 0 4 41 1.200391 1.759715 0.047865 900 " " X " " 21 0.00000 0.00000 "UNK " " " " " 1 0 0 1 "" 0 5 42 -0.612341 0.720066 0.012557 900 " " X " " 21 0.45000 0.45000 "UNK " " " " " 1 0 0 1 "" <> ::: } m_bond[8] { # First column is bond index # i_m_from i_m_to i_m_order i_m_from_rep i_m_to_rep ::: 1 1 2 2 1 1 2 1 3 1 1 1 3 1 4 1 1 1 4 2 1 2 1 1 5 3 1 1 1 1 6 3 5 1 1 1 7 4 1 1 1 1 8 5 3 1 1 1 ::: } } f_m_ct { s_m_title s_m_entry_name r_mmod_Potential_Energy-OPLS-2005 b_mmod_Minimization_Converged-OPLS-2005 r_mmod_RMS_Derivative-OPLS-2005 i_mmod_Times_Found-OPLS-2005 r_mmod_Relative_Potential_Energy-OPLS-2005 b_mmod_Chiralities_Consistent-OPLS-2005 i_mmod_Conformation-OPLS-2005 i_mmod_Serial_Number-OPLS-2005 i_sd_Mol\_ID s_sd_Formula r_sd_MolWeight i_sd_SchIID s_sd_Source i_sd_Source\_ID s_sd_Chemical_Name r_sd_pKa\_1 i_sd_Temp\_1 s_sd_Assessment\_1 s_sd_Identifier\_1 s_user_Data_Set s_m_entry_id ::: 2:Acids ligprep-out.2 1.90448498725891 0 10.1268978118896 1 0 1 1 2 2 CH2N2O4 106.03758 2 SB79 2008 "Methane, dinitro-" 3.57 25 Approx. methane_nitro_nitro Acids 2 m_depend[8] { # First column is dependency index # i_m_depend_dependency s_m_depend_property ::: 1 20 r_mmod_Potential_Energy-OPLS-2005 2 20 b_mmod_Minimization_Converged-OPLS-2005 3 20 r_mmod_RMS_Derivative-OPLS-2005 4 20 i_mmod_Times_Found-OPLS-2005 5 20 r_mmod_Relative_Potential_Energy-OPLS-2005 6 20 b_mmod_Chiralities_Consistent-OPLS-2005 7 20 i_mmod_Conformation-OPLS-2005 8 10 i_mmod_Serial_Number-OPLS-2005 ::: } m_atom[9] { # First column is atom index # i_m_mmod_type r_m_x_coord r_m_y_coord r_m_z_coord i_m_residue_number s_m_insertion_code s_m_mmod_res s_m_chain_name i_m_color r_m_charge1 r_m_charge2 s_m_pdb_residue_name s_m_pdb_atom_name s_m_grow_name i_m_atomic_number i_m_formal_charge i_m_representation i_m_visibility s_m_atom_name i_m_template_index ::: 1 3 2.454867 -0.068087 0.028385 900 " " X " " 2 0.28000 0.28000 "UNK " " " " " 6 0 0 1 "" 0 2 31 3.682039 0.774771 -0.121640 900 " " X " " 43 0.54000 0.54000 "UNK " " " " " 7 1 0 1 "" 0 3 15 3.664435 1.669564 -0.966160 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 0 0 1 "" 0 4 18 4.640647 0.508696 0.600789 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 -1 0 1 "" 0 5 31 1.220234 0.767491 0.155745 900 " " X " " 43 0.54000 0.54000 "UNK " " " " " 7 1 0 1 "" 0 6 15 1.203396 1.634407 1.028171 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 0 0 1 "" 0 7 18 0.292550 0.524092 -0.612877 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 -1 0 1 "" 0 8 41 2.368701 -0.700720 -0.855635 900 " " X " " 21 0.06000 0.06000 "UNK " " " " " 1 0 0 1 "" 0 9 41 2.546380 -0.674243 0.930770 900 " " X " " 21 0.06000 0.06000 "UNK " " " " " 1 0 0 1 "" 0 ::: } m_bond[16] { # First column is bond index # i_m_from i_m_to i_m_order i_m_from_rep i_m_to_rep ::: 1 1 2 1 1 1 2 1 5 1 1 1 3 1 8 1 1 1 4 1 9 1 1 1 5 2 1 1 1 1 6 2 3 2 1 1 7 2 4 1 1 1 8 3 2 2 1 1 9 4 2 1 1 1 10 5 1 1 1 1 11 5 6 2 1 1 12 5 7 1 1 1 13 6 5 2 1 1 14 7 5 1 1 1 15 8 1 1 1 1 16 9 1 1 1 1 ::: } } f_m_ct { s_m_title s_m_entry_name r_mmod_Potential_Energy-OPLS-2005 b_mmod_Minimization_Converged-OPLS-2005 r_mmod_RMS_Derivative-OPLS-2005 i_mmod_Times_Found-OPLS-2005 r_mmod_Relative_Potential_Energy-OPLS-2005 b_mmod_Chiralities_Consistent-OPLS-2005 i_mmod_Conformation-OPLS-2005 i_mmod_Serial_Number-OPLS-2005 i_sd_Mol\_ID s_sd_Formula r_sd_MolWeight i_sd_SchIID s_sd_Source i_sd_Source\_ID s_sd_Chemical_Name r_sd_pKa\_1 i_sd_Temp\_1 s_sd_Assessment\_1 s_sd_Identifier\_1 s_user_Data_Set s_m_entry_id ::: 3:Acids ligprep-out.3 0.36353063583374 0 0.394437789916992 1 0 1 1 3 3 CH3NO2 61.04002 3 SB79 2009 "Methane, nitro-" 10.24 25 Approx. methane_nitro Acids 3 m_depend[8] { # First column is dependency index # i_m_depend_dependency s_m_depend_property ::: 1 20 r_mmod_Potential_Energy-OPLS-2005 2 20 b_mmod_Minimization_Converged-OPLS-2005 3 20 r_mmod_RMS_Derivative-OPLS-2005 4 20 i_mmod_Times_Found-OPLS-2005 5 20 r_mmod_Relative_Potential_Energy-OPLS-2005 6 20 b_mmod_Chiralities_Consistent-OPLS-2005 7 20 i_mmod_Conformation-OPLS-2005 8 10 i_mmod_Serial_Number-OPLS-2005 ::: } m_atom[7] { # First column is atom index # i_m_mmod_type r_m_x_coord r_m_y_coord r_m_z_coord i_m_residue_number s_m_insertion_code s_m_mmod_res s_m_chain_name i_m_color r_m_charge1 r_m_charge2 s_m_pdb_residue_name s_m_pdb_atom_name s_m_grow_name i_m_atomic_number i_m_formal_charge i_m_representation i_m_visibility s_m_atom_name i_m_template_index ::: 1 3 2.130632 2.462572 0.019139 900 " " X " " 2 0.02000 0.02000 "UNK " " " " " 6 0 0 1 "" 0 2 31 1.375937 1.174885 0.028298 900 " " X " " 43 0.54000 0.54000 "UNK " " " " " 7 1 0 1 "" 0 3 15 2.019597 0.136895 -0.075313 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 0 0 1 "" 0 4 18 0.156828 1.237457 0.136330 900 " " X " " 70 -0.37000 -0.37000 "UNK " " " " " 8 -1 0 1 "" 0 5 41 3.196829 2.244744 0.089627 900 " " X " " 21 0.06000 0.06000 "UNK " " " " " 1 0 0 1 "" 0 6 41 1.902515 2.975416 -0.915329 900 " " X " " 21 0.06000 0.06000 "UNK " " " " " 1 0 0 1 "" 0 7 41 1.801453 3.051384 0.875647 900 " " X " " 21 0.06000 0.06000 "UNK " " " " " 1 0 0 1 "" 0 ::: } m_bond[12] { # First column is bond index # i_m_from i_m_to i_m_order i_m_from_rep i_m_to_rep ::: 1 1 2 1 1 1 2 1 5 1 1 1 3 1 6 1 1 1 4 1 7 1 1 1 5 2 1 1 1 1 6 2 3 2 1 1 7 2 4 1 1 1 8 3 2 2 1 1 9 4 2 1 1 1 10 5 1 1 1 1 11 6 1 1 1 1 12 7 1 1 1 1 ::: } } maeparser-1.3.1/test/test2.maegz000066400000000000000000000034171436653602000165520ustar00rootroot00000000000000~5.[test2.maeYnF}Wk-bRA6NPh d @Ү oZɎm ggsȏIREZLo%4ÕL% ̚cM,2/g˦K4TiQM@s>],ΎB0*_EaI/,}U `o"wyzW-urWE̪ڌ eU'0uqU#@ (ӗϬ9^Lծ,jlvC,k]_UV33{Xh\:}0mVܭ񴮳.Y{/_yV:gf O ^yg,&VGu3a=3A†")c{j֬>,kGY߈q>55zG h2r 0b=G;\O,Z S`2ȑΕ5tsٹRG{gf4~ip@.R5;j 엮 '//33߸#m'Aw@zIg^'& '3A1Q)OzQ kURTrM=a)E췢oHzמfbڴSk N E SwQ D,|cTZ-_vC^6jZrfKzsJbQSk0 qLܽĎ|_N nDh;b'Ε2 pͦ(