From f4c3a108acc969ca4639016e19c056ff10921daa Mon Sep 17 00:00:00 2001 From: fannygustafsson <fannygu@chalmers.se> Date: Tue, 4 Jun 2024 13:36:27 +0200 Subject: [PATCH] prepared for first review --- .gitignore | 36 +- .gitlab-ci.yml | 45 +- CMakeLists.txt | 132 +- Dockerfile | 43 +- FindCluonMsc.cmake | 24 + FindLibRT.cmake | 121 - ....0.120.hpp => cluon-complete-v0.0.148.hpp} | 6764 ++++++++++------- src/opendlv-message-standard-1.0.odvd | 429 ++ src/opendlv-standard-message-set-v0.9.6.odvd | 366 - src/opendlv-statsd.cpp | 114 +- 10 files changed, 4478 insertions(+), 3596 deletions(-) create mode 100644 FindCluonMsc.cmake delete mode 100644 FindLibRT.cmake rename src/{cluon-complete-v0.0.120.hpp => cluon-complete-v0.0.148.hpp} (80%) create mode 100644 src/opendlv-message-standard-1.0.odvd delete mode 100644 src/opendlv-standard-message-set-v0.9.6.odvd diff --git a/.gitignore b/.gitignore index 259148f..4f76550 100644 --- a/.gitignore +++ b/.gitignore @@ -1,32 +1,6 @@ -# Prerequisites -*.d +build +b -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app +.DS_store +.vscode +.clang-format \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d0d77ab..d300cdb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,29 +1,12 @@ -# Copyright (C) 2022 Christian Berger, Ola Benderius -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# Copyright (C) 2024 OpenDLV -image: docker:$CI_HOST_DOCKER_VERSION +image: docker variables: - DOCKER_HOST: tcp://docker:2375 - DOCKER_TLS_CERTDIR: "" - DOCKER_CLI_EXPERIMENTAL: enabled PLATFORMS: "linux/amd64,linux/arm64,linux/arm/v7" services: - - name: docker:${CI_HOST_DOCKER_VERSION}-dind - command: ["--experimental"] + - name: docker:dind stages: - build @@ -38,24 +21,16 @@ build-amd64: stage: build script: - docker build . - only: - - master + rules: + - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH -# Release product using the Dockerfiles for the platforms. release: tags: - docker-build stage: deploy script: - - docker run --privileged linuxkit/binfmt:v0.7 - - apk update && apk add curl - - curl -L "https://github.com/docker/buildx/releases/download/v0.3.1/buildx-v0.3.1.linux-amd64" --output "/tmp/docker-buildx" && chmod 755 /tmp/docker-buildx - - /tmp/docker-buildx create --name multiplatformbuilder - - /tmp/docker-buildx use multiplatformbuilder - - /tmp/docker-buildx build --platform "$PLATFORMS" -t "$CI_REGISTRY_IMAGE":"$CI_COMMIT_TAG" . && - /tmp/docker-buildx build --platform "$PLATFORMS" -t "$CI_REGISTRY_IMAGE":"$CI_COMMIT_TAG" --push . - only: - - tags - - /^v[0-9.]+$/ - when: on_success - + - docker run --privileged --rm tonistiigi/binfmt --install all + - docker buildx create --name multiplatformbuilder --use + - docker buildx build --platform "$PLATFORMS" -t "$CI_REGISTRY_IMAGE":"$CI_COMMIT_TAG" --push . + rules: + - if: $CI_COMMIT_TAG =~ /^[0-9.]+$/ \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index cad54e4..ee8603f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,93 +1,77 @@ -# Copyright (C) 2019 Christian Berger -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# Copyright (C) 2024 OpenDLV -cmake_minimum_required(VERSION 3.2) +cmake_minimum_required(VERSION 3.10) project(opendlv-statsd) -################################################################################ -# Defining the relevant versions of OpenDLV Standard Message Set and libcluon. -set(OPENDLV_STANDARD_MESSAGE_SET opendlv-standard-message-set-v0.9.6.odvd) -set(CLUON_COMPLETE cluon-complete-v0.0.120.hpp) +set(OPENDLV_MESSAGE_STANDARD opendlv-message-standard-1.0.odvd) +set(CLUON_COMPLETE cluon-complete-v0.0.148.hpp) -################################################################################ -# Set the search path for .cmake files. -set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_MODULE_PATH}) - -################################################################################ -# This project requires C++14 or newer. -set(CMAKE_CXX_STANDARD 14) +set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -# Build a static binary. set(CMAKE_EXE_LINKER_FLAGS "-static-libgcc -static-libstdc++") -# Add further warning levels. +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} \ + -D_FORTIFY_SOURCE=2 -O3 -s") +set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} \ + -g -fsanitize=thread") + # -fsanitize=address set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} \ - -D_XOPEN_SOURCE=700 \ - -D_FORTIFY_SOURCE=2 \ - -O2 \ - -fstack-protector \ - -fomit-frame-pointer \ - -pipe \ - -Weffc++ \ - -Wall -Wextra -Wshadow -Wdeprecated \ - -Wdiv-by-zero -Wfloat-equal -Wfloat-conversion -Wsign-compare -Wpointer-arith \ - -Wuninitialized -Wunreachable-code \ - -Wunused -Wunused-function -Wunused-label -Wunused-parameter -Wunused-but-set-parameter -Wunused-but-set-variable \ + -D_XOPEN_SOURCE=700 -fstack-protector \ + -fomit-frame-pointer -pipe -pedantic -pedantic-errors -Werror -Weffc++ \ + -Wall -Wextra -Wshadow -Wdeprecated -Wdiv-by-zero -Wfloat-equal \ + -Wfloat-conversion -Wsign-compare -Wpointer-arith -Wuninitialized \ + -Wunreachable-code -Wunused -Wunused-function -Wunused-label \ + -Wunused-parameter -Wunused-but-set-parameter -Wunused-but-set-variable \ -Wunused-value -Wunused-variable -Wunused-result \ - -Wmissing-field-initializers -Wmissing-format-attribute -Wmissing-include-dirs -Wmissing-noreturn") -# Threads are necessary for linking the resulting binaries as UDPReceiver is running in parallel. -set(THREADS_PREFER_PTHREAD_FLAG ON) -find_package(Threads REQUIRED) + -Wmissing-field-initializers -Wmissing-format-attribute \ + -Wmissing-include-dirs -Wmissing-noreturn") -################################################################################ -# Extract cluon-msc from cluon-complete.hpp. -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-msc - WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE} ${CMAKE_BINARY_DIR}/cluon-complete.hpp - COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_BINARY_DIR}/cluon-complete.hpp ${CMAKE_BINARY_DIR}/cluon-complete.cpp - COMMAND ${CMAKE_CXX_COMPILER} -o ${CMAKE_BINARY_DIR}/cluon-msc ${CMAKE_BINARY_DIR}/cluon-complete.cpp -std=c++14 -pthread -D HAVE_CLUON_MSC +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}") +find_package(CluonMsc) + +if (NOT CLUONMSC_FOUND) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-msc + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${CMAKE_BINARY_DIR}/cluon-complete.hpp + ${CMAKE_BINARY_DIR}/cluon-complete.cpp + COMMAND ${CMAKE_CXX_COMPILER} -o ${CMAKE_BINARY_DIR}/cluon-msc + ${CMAKE_BINARY_DIR}/cluon-complete.cpp -std=c++20 -pthread + -D HAVE_CLUON_MSC + DEPENDS ${CMAKE_BINARY_DIR}/cluon-complete.hpp) +else (NOT CLUONMSC_FOUND) + add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-msc + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${CLUONMSC_PATH} ${CMAKE_BINARY_DIR}/cluon-msc + DEPENDS ${CMAKE_BINARY_DIR}/cluon-complete.hpp) +endif (NOT CLUONMSC_FOUND) + +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/cluon-complete.hpp + COMMAND ${CMAKE_COMMAND} -E create_symlink + ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE} + ${CMAKE_BINARY_DIR}/cluon-complete.hpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${CLUON_COMPLETE}) -################################################################################ -# Generate opendlv-standard-message-set.hpp from ${OPENDLV_STANDARD_MESSAGE_SET} file. -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/opendlv-message-standard.hpp WORKING_DIRECTORY ${CMAKE_BINARY_DIR} - COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp --out=${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_STANDARD_MESSAGE_SET} ${CMAKE_BINARY_DIR}/cluon-msc) -# Add current build directory as include directory as it contains generated files. + COMMAND ${CMAKE_BINARY_DIR}/cluon-msc --cpp + --out=${CMAKE_BINARY_DIR}/opendlv-message-standard.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_MESSAGE_STANDARD} + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/${OPENDLV_MESSAGE_STANDARD} + ${CMAKE_BINARY_DIR}/cluon-msc) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) + include_directories(SYSTEM ${CMAKE_BINARY_DIR}) include_directories(${CMAKE_CURRENT_SOURCE_DIR}/src) -################################################################################ -# Gather all object code first to avoid double compilation. -set(LIBRARIES Threads::Threads) - -if(UNIX) - if(NOT "${CMAKE_SYSTEM_NAME}" STREQUAL "Darwin") - find_package(LibRT REQUIRED) - set(LIBRARIES ${LIBRARIES} ${LIBRT_LIBRARIES}) - include_directories(SYSTEM ${LIBRT_INCLUDE_DIR}) - endif() -endif() +add_executable(${PROJECT_NAME} + ${CMAKE_CURRENT_SOURCE_DIR}/src/${PROJECT_NAME}.cpp + ${CMAKE_BINARY_DIR}/opendlv-message-standard.hpp) -################################################################################ -# Create executable. -add_executable(${PROJECT_NAME} ${CMAKE_CURRENT_SOURCE_DIR}/src/${PROJECT_NAME}.cpp ${CMAKE_BINARY_DIR}/opendlv-standard-message-set.hpp) -target_link_libraries(${PROJECT_NAME} ${LIBRARIES}) +target_link_libraries(${PROJECT_NAME} + Threads::Threads) -################################################################################ -# Install executable. -install(TARGETS ${PROJECT_NAME} DESTINATION bin COMPONENT ${PROJECT_NAME}) +install(TARGETS ${PROJECT_NAME} DESTINATION bin COMPONENT ${PROJECT_NAME})# Copyright (C) 2024 OpenDLV \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index ec631ad..5d37feb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,38 +1,25 @@ -# Copyright (C) 2019 Christian Berger -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. +# Copyright (C) 2024 OpenDLV -FROM chrberger/cluon-amd64:latest as builder -MAINTAINER Christian Berger "christian.berger@gu.se" +FROM alpine:3.19 as builder RUN apk update && \ apk --no-cache add \ cmake \ g++ \ - make -ADD . /opt/sources -WORKDIR /opt/sources -RUN mkdir build && \ - cd build && \ - cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/tmp .. && \ - make && make install + make \ + linux-headers + +ADD CMakeLists.txt FindCluonMsc.cmake /opt/sources/ +ADD src /opt/sources/src +RUN mkdir /opt/build /opt/out && \ + cd /opt/build && \ + cmake -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/opt/out \ + /opt/sources && \ + make && make install -FROM chrberger/cluon-amd64:latest -MAINTAINER Christian Berger "christian.berger@gu.se" -WORKDIR /usr/bin -COPY --from=builder /tmp/bin/opendlv-statsd . -ENTRYPOINT ["/usr/bin/opendlv-statsd"] +FROM alpine:3.19 +COPY --from=builder /opt/out/ /usr +ENTRYPOINT ["/usr/bin/opendlv-statsd"] \ No newline at end of file diff --git a/FindCluonMsc.cmake b/FindCluonMsc.cmake new file mode 100644 index 0000000..ec5ebb4 --- /dev/null +++ b/FindCluonMsc.cmake @@ -0,0 +1,24 @@ +# Copyright (C) 2024 OpenDLV + +if (NOT CLUONMSC_FOUND) + find_file( + CLUONMSC_PATH cluon-msc + PATHS + /usr/bin + /usr/local/bin) + + if (CLUONMSC_PATH) + set (CLUONMSC_FOUND TRUE) + endif (CLUONMSC_PATH) + + if (CLUONMSC_FOUND) + message(STATUS "Found cluon-msc: ${CLUONMSC_PATH}") + else (CLUONMSC_FOUND) + if (CluonMsc_FIND_REQUIRED) + message (FATAL_ERROR "Could not find cluon-msc") + else (CluonMsc_FIND_REQUIRED) + message(STATUS "Proceeding without cluon-msc") + endif (CluonMsc_FIND_REQUIRED) + endif (CLUONMSC_FOUND) + +endif (NOT CLUONMSC_FOUND) diff --git a/FindLibRT.cmake b/FindLibRT.cmake deleted file mode 100644 index 1fe94d9..0000000 --- a/FindLibRT.cmake +++ /dev/null @@ -1,121 +0,0 @@ -# You may redistribute this program and/or modify it under the terms of -# the GNU General Public License as published by the Free Software Foundation, -# either version 3 of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see <http://www.gnu.org/licenses/>. - -if(NOT LIBRT_FOUND) - - IF(${CMAKE_C_COMPILER} MATCHES "arm") - # We are on ARM. - find_path(LIBRT_INCLUDE_DIR - NAMES - time.h - PATHS - ${LIBRTDIR}/include/ - ) - - find_file( - LIBRT_LIBRARIES librt.a - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/arm-linux-gnueabihf/ - /usr/lib/arm-linux-gnueabi/ - ) - set (LIBRT_DYNAMIC "Using static library.") - - if (NOT LIBRT_LIBRARIES) - find_library( - LIBRT_LIBRARIES rt - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/arm-linux-gnueabihf/ - /usr/lib/arm-linux-gnueabi/ - ) - set (LIBRT_DYNAMIC "Using dynamic library.") - endif (NOT LIBRT_LIBRARIES) - ELSE() - IF("${CMAKE_SIZEOF_VOID_P}" STREQUAL "8") - # We are on x86_64. - find_path(LIBRT_INCLUDE_DIR - NAMES - time.h - PATHS - ${LIBRTDIR}/include/ - ) - - find_file( - LIBRT_LIBRARIES librt.a - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/x86_64-linux-gnu/ - /usr/local/lib64/ - /usr/lib64/ - /usr/lib/ - ) - set (LIBRT_DYNAMIC "Using static library.") - - if (NOT LIBRT_LIBRARIES) - find_library( - LIBRT_LIBRARIES rt - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/x86_64-linux-gnu/ - /usr/local/lib64/ - /usr/lib64/ - /usr/lib/ - ) - set (LIBRT_DYNAMIC "Using dynamic library.") - endif (NOT LIBRT_LIBRARIES) - ELSE() - # We are on x86. - find_path(LIBRT_INCLUDE_DIR - NAMES - time.h - PATHS - ${LIBRTDIR}/include/ - ) - - find_file( - LIBRT_LIBRARIES librt.a - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/i386-linux-gnu/ - /usr/local/lib/ - /usr/lib/ - ) - set (LIBRT_DYNAMIC "Using static library.") - - if (NOT LIBRT_LIBRARIES) - find_library( - LIBRT_LIBRARIES rt - PATHS - ${LIBRTDIR}/lib/ - /usr/lib/i386-linux-gnu/ - /usr/local/lib/ - /usr/lib/ - ) - set (LIBRT_DYNAMIC "Using dynamic library.") - endif (NOT LIBRT_LIBRARIES) - ENDIF() - ENDIF() - - if (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES) - set (LIBRT_FOUND TRUE) - endif (LIBRT_INCLUDE_DIR AND LIBRT_LIBRARIES) - - if (LIBRT_FOUND) - message(STATUS "Found librt: ${LIBRT_INCLUDE_DIR}, ${LIBRT_LIBRARIES} ${LIBRT_DYNAMIC}") - else (LIBRT_FOUND) - if (Librt_FIND_REQUIRED) - message (FATAL_ERROR "Could not find librt, try to setup LIBRT_PREFIX accordingly") - endif (Librt_FIND_REQUIRED) - endif (LIBRT_FOUND) - -endif (NOT LIBRT_FOUND) diff --git a/src/cluon-complete-v0.0.120.hpp b/src/cluon-complete-v0.0.148.hpp similarity index 80% rename from src/cluon-complete-v0.0.120.hpp rename to src/cluon-complete-v0.0.148.hpp index 9d00453..857225c 100644 --- a/src/cluon-complete-v0.0.120.hpp +++ b/src/cluon-complete-v0.0.148.hpp @@ -1,6 +1,6 @@ // This is an auto-generated header-only single-file distribution of libcluon. -// Date: Fri, 28 Dec 2018 21:22:34 +0100 -// Version: 0.0.120 +// Date: Fri, 22 Mar 2024 10:44:53 +0100 +// Version: 0.0.148 // // // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. @@ -20,10 +20,33 @@ #ifndef LINB_ANY_HPP #define LINB_ANY_HPP //#pragma once +#if defined(__APPLE__) + #include <utility> +#endif #include <typeinfo> #include <type_traits> #include <stdexcept> + +#if defined(PARTICLE) +#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS) +# define ANY_IMPL_NO_EXCEPTIONS +# endif +#else +// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, +// but you must ensure not to cast badly when passing an `any' object to any_cast<T>(any) +#endif + +#if defined(PARTICLE) +#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI) +# define ANY_IMPL_NO_RTTI +# endif +#else +// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, +// in order to disable functions working with the typeid of a type +#endif + + namespace linb { @@ -131,11 +154,13 @@ public: return this->vtable == nullptr; } +#ifndef ANY_IMPL_NO_RTTI /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). const std::type_info& type() const noexcept { return empty()? typeid(void) : this->vtable->type(); } +#endif /// Exchange the states of *this and rhs. void swap(any& rhs) noexcept @@ -149,7 +174,7 @@ public: if(this->vtable != nullptr) { this->vtable->move(this->storage, rhs.storage); - //this->vtable = nullptr; -- uneeded, see below + //this->vtable = nullptr; -- unneeded, see below } // move from tmp (previously rhs) to *this. @@ -183,8 +208,10 @@ private: // Storage and Virtual Method Table // Note: The caller is responssible for doing .vtable = nullptr after destructful operations // such as destroy() and/or move(). +#ifndef ANY_IMPL_NO_RTTI /// The type of the object this vtable is for. const std::type_info& (*type)() noexcept; +#endif /// Destroys the object in the union. /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. @@ -206,10 +233,12 @@ private: // Storage and Virtual Method Table template<typename T> struct vtable_dynamic { +#ifndef ANY_IMPL_NO_RTTI static const std::type_info& type() noexcept { return typeid(T); } +#endif static void destroy(storage_union& storage) noexcept { @@ -239,10 +268,12 @@ private: // Storage and Virtual Method Table template<typename T> struct vtable_stack { +#ifndef ANY_IMPL_NO_RTTI static const std::type_info& type() noexcept { return typeid(T); } +#endif static void destroy(storage_union& storage) noexcept { @@ -275,7 +306,7 @@ private: // Storage and Virtual Method Table template<typename T> struct requires_allocation : std::integral_constant<bool, - !(std::is_nothrow_move_constructible<T>::value // N4562 �6.3/3 [any.class] + !(std::is_nothrow_move_constructible<T>::value // N4562 §6.3/3 [any.class] && sizeof(T) <= sizeof(storage_union::stack) && std::alignment_of<T>::value <= std::alignment_of<storage_union::stack_storage_t>::value)> {}; @@ -286,7 +317,10 @@ private: // Storage and Virtual Method Table { using VTableType = typename std::conditional<requires_allocation<T>::value, vtable_dynamic<T>, vtable_stack<T>>::type; static vtable_type table = { - VTableType::type, VTableType::destroy, +#ifndef ANY_IMPL_NO_RTTI + VTableType::type, +#endif + VTableType::destroy, VTableType::copy, VTableType::move, VTableType::swap, }; @@ -299,27 +333,6 @@ protected: template<typename T> friend T* any_cast(any* operand) noexcept; - /// Same effect as is_same(this->type(), t); - bool is_typed(const std::type_info& t) const - { - return is_same(this->type(), t); - } - - /// Checks if two type infos are the same. - /// - /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the - /// type infos, otherwise does an actual comparision. Checking addresses is - /// only a valid approach when there's no interaction with outside sources - /// (other shared libraries and such). - static bool is_same(const std::type_info& a, const std::type_info& b) - { -#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE - return &a == &b; -#else - return a == b; -#endif - } - /// Casts (with no type_info checks) the storage pointer as const T*. template<typename T> const T* cast() const noexcept @@ -391,7 +404,9 @@ template<typename ValueType> inline ValueType any_cast(const any& operand) { auto p = any_cast<typename std::add_const<typename std::remove_reference<ValueType>::type>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); +#endif return *p; } @@ -400,56 +415,55 @@ template<typename ValueType> inline ValueType any_cast(any& operand) { auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); +#endif return *p; } /// -/// If ANY_IMPL_ANYCAST_MOVEABLE is not defined, does as N4562 specifies: -/// Performs *any_cast<remove_reference_t<ValueType>>(&operand), or throws bad_any_cast on failure. -/// -/// If ANY_IMPL_ANYCAST_MOVEABLE is defined, does as LWG Defect 2509 specifies: -/// If ValueType is MoveConstructible and isn't a lvalue reference, performs -/// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise -/// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure. +/// If ValueType is MoveConstructible and isn't a lvalue reference, performs +/// std::move(*any_cast<remove_reference_t<ValueType>>(&operand)), otherwise +/// *any_cast<remove_reference_t<ValueType>>(&operand). Throws bad_any_cast on failure. /// template<typename ValueType> inline ValueType any_cast(any&& operand) { -#ifdef ANY_IMPL_ANY_CAST_MOVEABLE - // https://cplusplus.github.io/LWG/lwg-active.html#2509 using can_move = std::integral_constant<bool, std::is_move_constructible<ValueType>::value && !std::is_lvalue_reference<ValueType>::value>; -#else - using can_move = std::false_type; -#endif auto p = any_cast<typename std::remove_reference<ValueType>::type>(&operand); +#ifndef ANY_IMPL_NO_EXCEPTIONS if(p == nullptr) throw bad_any_cast(); +#endif return detail::any_cast_move_if_true<ValueType>(p, can_move()); } /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object /// contained by operand, otherwise nullptr. -template<typename T> -inline const T* any_cast(const any* operand) noexcept +template<typename ValueType> +inline const ValueType* any_cast(const any* operand) noexcept { - if(operand == nullptr || !operand->is_typed(typeid(T))) - return nullptr; + using T = typename std::decay<ValueType>::type; + + if (operand && operand->vtable == any::vtable_for_type<T>()) + return operand->cast<ValueType>(); else - return operand->cast<T>(); + return nullptr; } /// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object /// contained by operand, otherwise nullptr. -template<typename T> -inline T* any_cast(any* operand) noexcept +template<typename ValueType> +inline ValueType* any_cast(any* operand) noexcept { - if(operand == nullptr || !operand->is_typed(typeid(T))) - return nullptr; + using T = typename std::decay<ValueType>::type; + + if (operand && operand->vtable == any::vtable_for_type<T>()) + return operand->cast<ValueType>(); else - return operand->cast<T>(); + return nullptr; } } @@ -466,208 +480,174 @@ namespace std // // peglib.h // -// Copyright (c) 2015-18 Yuji Hirose. All rights reserved. +// Copyright (c) 2020 Yuji Hirose. All rights reserved. // MIT License // #ifndef CPPPEGLIB_PEGLIB_H #define CPPPEGLIB_PEGLIB_H +#ifndef PEGLIB_USE_STD_ANY +#ifdef _MSVC_LANG +#define PEGLIB_USE_STD_ANY _MSVC_LANG >= 201703L +#elif defined(__cplusplus) +#define PEGLIB_USE_STD_ANY __cplusplus >= 201703L +#endif +#endif // PEGLIB_USE_STD_ANY + #include <algorithm> #include <cassert> +#include <cctype> #include <cstring> #include <functional> #include <initializer_list> #include <iostream> #include <limits> +#include <list> #include <map> #include <memory> #include <mutex> #include <set> +#include <sstream> #include <string> #include <unordered_map> #include <vector> +#if PEGLIB_USE_STD_ANY +#include <any> +#endif // guard for older versions of VC++ #ifdef _MSC_VER -// VS2013 has no constexpr -#if (_MSC_VER == 1800) -#define PEGLIB_NO_CONSTEXPR_SUPPORT -#elif (_MSC_VER >= 1800) -// good to go -#else (_MSC_VER < 1800) -#error "Requires C+11 support" +#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 +#error "Requires complete C+11 support" #endif #endif -// define if the compiler doesn't support unicode characters reliably in the -// source code -//#define PEGLIB_NO_UNICODE_CHARS - namespace peg { -#if __clang__ == 1 && __clang_major__ <= 5 -static void* enabler = nullptr; // workaround for Clang version <= 5.0.0 -#else -extern void* enabler; -#endif - /*----------------------------------------------------------------------------- * any *---------------------------------------------------------------------------*/ -class any -{ -public: - any() : content_(nullptr) {} - - any(const any& rhs) : content_(rhs.clone()) {} +#if PEGLIB_USE_STD_ANY +using any = std::any; - any(any&& rhs) : content_(rhs.content_) { - rhs.content_ = nullptr; - } +// Define a function alias to std::any_cast using perfect forwarding +template <typename T, typename... Args> +auto any_cast(Args &&... args) + -> decltype(std::any_cast<T>(std::forward<Args>(args)...)) { + return std::any_cast<T>(std::forward<Args>(args)...); +} +#else +class any { +public: + any() = default; - template <typename T> - any(const T& value) : content_(new holder<T>(value)) {} + any(const any &rhs) : content_(rhs.clone()) {} - any& operator=(const any& rhs) { - if (this != &rhs) { - if (content_) { - delete content_; - } - content_ = rhs.clone(); - } - return *this; - } + any(any &&rhs) : content_(rhs.content_) { rhs.content_ = nullptr; } - any& operator=(any&& rhs) { - if (this != &rhs) { - if (content_) { - delete content_; - } - content_ = rhs.content_; - rhs.content_ = nullptr; - } - return *this; - } + template <typename T> any(const T &value) : content_(new holder<T>(value)) {} - ~any() { - delete content_; + any &operator=(const any &rhs) { + if (this != &rhs) { + if (content_) { delete content_; } + content_ = rhs.clone(); } + return *this; + } - bool is_undefined() const { - return content_ == nullptr; + any &operator=(any &&rhs) { + if (this != &rhs) { + if (content_) { delete content_; } + content_ = rhs.content_; + rhs.content_ = nullptr; } + return *this; + } - template < - typename T, - typename std::enable_if<!std::is_same<T, any>::value>::type*& = enabler - > - T& get() { - if (!content_) { - throw std::bad_cast(); - } - auto p = dynamic_cast<holder<T>*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } + ~any() { delete content_; } - template < - typename T, - typename std::enable_if<std::is_same<T, any>::value>::type*& = enabler - > - T& get() { - return *this; - } + bool has_value() const { return content_ != nullptr; } - template < - typename T, - typename std::enable_if<!std::is_same<T, any>::value>::type*& = enabler - > - const T& get() const { - assert(content_); - auto p = dynamic_cast<holder<T>*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } + template <typename T> friend T &any_cast(any &val); - template < - typename T, - typename std::enable_if<std::is_same<T, any>::value>::type*& = enabler - > - const any& get() const { - return *this; - } + template <typename T> friend const T &any_cast(const any &val); private: - struct placeholder { - virtual ~placeholder() {} - virtual placeholder* clone() const = 0; - }; + struct placeholder { + virtual ~placeholder() {} + virtual placeholder *clone() const = 0; + }; - template <typename T> - struct holder : placeholder { - holder(const T& value) : value_(value) {} - placeholder* clone() const override { - return new holder(value_); - } - T value_; - }; + template <typename T> struct holder : placeholder { + holder(const T &value) : value_(value) {} + placeholder *clone() const override { return new holder(value_); } + T value_; + }; - placeholder* clone() const { - return content_ ? content_->clone() : nullptr; - } + placeholder *clone() const { return content_ ? content_->clone() : nullptr; } - placeholder* content_; + placeholder *content_ = nullptr; }; +template <typename T> T &any_cast(any &val) { + if (!val.content_) { throw std::bad_cast(); } + auto p = dynamic_cast<any::holder<T> *>(val.content_); + assert(p); + if (!p) { throw std::bad_cast(); } + return p->value_; +} + +template <> inline any &any_cast<any>(any &val) { return val; } + +template <typename T> const T &any_cast(const any &val) { + assert(val.content_); + auto p = dynamic_cast<any::holder<T> *>(val.content_); + assert(p); + if (!p) { throw std::bad_cast(); } + return p->value_; +} + +template <> inline const any &any_cast<any>(const any &val) { return val; } +#endif + /*----------------------------------------------------------------------------- * scope_exit *---------------------------------------------------------------------------*/ -// This is based on "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". +// This is based on +// "http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4189". -template <typename EF> -struct scope_exit -{ - explicit scope_exit(EF&& f) - : exit_function(std::move(f)) - , execute_on_destruction{true} {} +template <typename EF> struct scope_exit { + explicit scope_exit(EF &&f) + : exit_function(std::move(f)), execute_on_destruction{true} {} - scope_exit(scope_exit&& rhs) - : exit_function(std::move(rhs.exit_function)) - , execute_on_destruction{rhs.execute_on_destruction} { - rhs.release(); - } + scope_exit(scope_exit &&rhs) + : exit_function(std::move(rhs.exit_function)), + execute_on_destruction{rhs.execute_on_destruction} { + rhs.release(); + } - ~scope_exit() { - if (execute_on_destruction) { - this->exit_function(); - } - } + ~scope_exit() { + if (execute_on_destruction) { this->exit_function(); } + } - void release() { - this->execute_on_destruction = false; - } + void release() { this->execute_on_destruction = false; } private: - scope_exit(const scope_exit&) = delete; - void operator=(const scope_exit&) = delete; - scope_exit& operator=(scope_exit&&) = delete; + scope_exit(const scope_exit &) = delete; + void operator=(const scope_exit &) = delete; + scope_exit &operator=(scope_exit &&) = delete; - EF exit_function; - bool execute_on_destruction; + EF exit_function; + bool execute_on_destruction; }; template <typename EF> -auto make_scope_exit(EF&& exit_function) -> scope_exit<EF> { - return scope_exit<typename std::remove_reference<EF>::type>(std::forward<EF>(exit_function)); +auto make_scope_exit(EF &&exit_function) -> scope_exit<EF> { + return scope_exit<typename std::remove_reference<EF>::type>( + std::forward<EF>(exit_function)); } /*----------------------------------------------------------------------------- @@ -766,9 +746,7 @@ inline bool decode_codepoint(const char *s8, size_t l, size_t &bytes, inline size_t decode_codepoint(const char *s8, size_t l, char32_t &out) { size_t bytes; - if (decode_codepoint(s8, l, bytes, out)) { - return bytes; - } + if (decode_codepoint(s8, l, bytes, out)) { return bytes; } return 0; } @@ -795,423 +773,521 @@ inline std::u32string decode(const char *s8, size_t l) { * resolve_escape_sequence *---------------------------------------------------------------------------*/ -inline bool is_hex(char c, int& v) { - if ('0' <= c && c <= '9') { - v = c - '0'; - return true; - } else if ('a' <= c && c <= 'f') { - v = c - 'a' + 10; - return true; - } else if ('A' <= c && c <= 'F') { - v = c - 'A' + 10; - return true; - } - return false; +inline bool is_hex(char c, int &v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } else if ('a' <= c && c <= 'f') { + v = c - 'a' + 10; + return true; + } else if ('A' <= c && c <= 'F') { + v = c - 'A' + 10; + return true; + } + return false; } -inline bool is_digit(char c, int& v) { - if ('0' <= c && c <= '9') { - v = c - '0'; - return true; - } - return false; +inline bool is_digit(char c, int &v) { + if ('0' <= c && c <= '9') { + v = c - '0'; + return true; + } + return false; } -inline std::pair<int, size_t> parse_hex_number(const char* s, size_t n, size_t i) { - int ret = 0; - int val; - while (i < n && is_hex(s[i], val)) { - ret = static_cast<int>(ret * 16 + val); - i++; - } - return std::make_pair(ret, i); +inline std::pair<int, size_t> parse_hex_number(const char *s, size_t n, + size_t i) { + int ret = 0; + int val; + while (i < n && is_hex(s[i], val)) { + ret = static_cast<int>(ret * 16 + val); + i++; + } + return std::make_pair(ret, i); +} + +inline std::pair<int, size_t> parse_octal_number(const char *s, size_t n, + size_t i) { + int ret = 0; + int val; + while (i < n && is_digit(s[i], val)) { + ret = static_cast<int>(ret * 8 + val); + i++; + } + return std::make_pair(ret, i); } -inline std::pair<int, size_t> parse_octal_number(const char* s, size_t n, size_t i) { - int ret = 0; - int val; - while (i < n && is_digit(s[i], val)) { - ret = static_cast<int>(ret * 8 + val); +inline std::string resolve_escape_sequence(const char *s, size_t n) { + std::string r; + r.reserve(n); + + size_t i = 0; + while (i < n) { + auto ch = s[i]; + if (ch == '\\') { + i++; + if (i == n) { throw std::runtime_error("Invalid escape sequence..."); } + switch (s[i]) { + case 'n': + r += '\n'; i++; + break; + case 'r': + r += '\r'; + i++; + break; + case 't': + r += '\t'; + i++; + break; + case '\'': + r += '\''; + i++; + break; + case '"': + r += '"'; + i++; + break; + case '[': + r += '['; + i++; + break; + case ']': + r += ']'; + i++; + break; + case '\\': + r += '\\'; + i++; + break; + case 'x': + case 'u': { + char32_t cp; + std::tie(cp, i) = parse_hex_number(s, n, i + 1); + r += encode_codepoint(cp); + break; + } + default: { + char32_t cp; + std::tie(cp, i) = parse_octal_number(s, n, i); + r += encode_codepoint(cp); + break; + } + } + } else { + r += ch; + i++; } - return std::make_pair(ret, i); + } + return r; } -inline std::string resolve_escape_sequence(const char* s, size_t n) { - std::string r; - r.reserve(n); +/*----------------------------------------------------------------------------- + * Trie + *---------------------------------------------------------------------------*/ - size_t i = 0; - while (i < n) { - auto ch = s[i]; - if (ch == '\\') { - i++; - switch (s[i]) { - case 'n': r += '\n'; i++; break; - case 'r': r += '\r'; i++; break; - case 't': r += '\t'; i++; break; - case '\'': r += '\''; i++; break; - case '"': r += '"'; i++; break; - case '[': r += '['; i++; break; - case ']': r += ']'; i++; break; - case '\\': r += '\\'; i++; break; - case 'x': - case 'u': { - char32_t cp; - std::tie(cp, i) = parse_hex_number(s, n, i + 1); - r += encode_codepoint(cp); - break; - } - default: { - char32_t cp; - std::tie(cp, i) = parse_octal_number(s, n, i); - r += encode_codepoint(cp); - break; - } - } +class Trie { +public: + Trie() = default; + Trie(const Trie &) = default; + + Trie(const std::vector<std::string> &items) { + for (const auto &item : items) { + for (size_t len = 1; len <= item.size(); len++) { + auto last = len == item.size(); + std::string s(item.c_str(), len); + auto it = dic_.find(s); + if (it == dic_.end()) { + dic_.emplace(s, Info{last, last}); + } else if (last) { + it->second.match = true; } else { - r += ch; - i++; + it->second.done = false; } + } } - return r; -} + } + + size_t match(const char *text, size_t text_len) const { + size_t match_len = 0; + { + auto done = false; + size_t len = 1; + while (!done && len <= text_len) { + std::string s(text, len); + auto it = dic_.find(s); + if (it == dic_.end()) { + done = true; + } else { + if (it->second.match) { match_len = len; } + if (it->second.done) { done = true; } + } + len += 1; + } + } + return match_len; + } + +private: + struct Info { + bool done; + bool match; + }; + std::unordered_map<std::string, Info> dic_; +}; /*----------------------------------------------------------------------------- * PEG *---------------------------------------------------------------------------*/ /* -* Line information utility function -*/ -inline std::pair<size_t, size_t> line_info(const char* start, const char* cur) { - auto p = start; - auto col_ptr = p; - auto no = 1; + * Line information utility function + */ +inline std::pair<size_t, size_t> line_info(const char *start, const char *cur) { + auto p = start; + auto col_ptr = p; + auto no = 1; - while (p < cur) { - if (*p == '\n') { - no++; - col_ptr = p + 1; - } - p++; + while (p < cur) { + if (*p == '\n') { + no++; + col_ptr = p + 1; } + p++; + } - auto col = p - col_ptr + 1; + auto col = p - col_ptr + 1; - return std::make_pair(no, col); + return std::make_pair(no, col); } /* -* Semantic values -*/ -struct SemanticValues : protected std::vector<any> -{ - // Input text - const char* path; - const char* ss; + * String tag + */ +inline constexpr unsigned int str2tag(const char *str, unsigned int h = 0) { + return (*str == '\0') + ? h + : str2tag(str + 1, (h * 33) ^ static_cast<unsigned char>(*str)); +} - // Matched string - const char* c_str() const { return s_; } - size_t length() const { return n_; } +namespace udl { - std::string str() const { - return std::string(s_, n_); - } +inline constexpr unsigned int operator"" _(const char *s, size_t) { + return str2tag(s); +} - // Line number and column at which the matched string is - std::pair<size_t, size_t> line_info() const { - return peg::line_info(ss, s_); - } +} // namespace udl - // Choice count - size_t choice_count() const { return choice_count_; } +/* + * Semantic values + */ +struct SemanticValues : protected std::vector<any> { + // Input text + const char *path = nullptr; + const char *ss = nullptr; + const std::vector<size_t> *source_line_index = nullptr; - // Choice number (0 based index) - size_t choice() const { return choice_; } + // Matched string + const char *c_str() const { return s_; } + size_t length() const { return n_; } - // Tokens - std::vector<std::pair<const char*, size_t>> tokens; + std::string str() const { return std::string(s_, n_); } - std::string token(size_t id = 0) const { - if (!tokens.empty()) { - assert(id < tokens.size()); - const auto& tok = tokens[id]; - return std::string(tok.first, tok.second); - } - return std::string(s_, n_); - } + // Definition name + const std::string &name() const { return name_; } - // Transform the semantic value vector to another vector - template <typename T> - auto transform(size_t beg = 0, size_t end = static_cast<size_t>(-1)) const -> vector<T> { - return this->transform(beg, end, [](const any& v) { return v.get<T>(); }); - } - - SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {} - - using std::vector<any>::iterator; - using std::vector<any>::const_iterator; - using std::vector<any>::size; - using std::vector<any>::empty; - using std::vector<any>::assign; - using std::vector<any>::begin; - using std::vector<any>::end; - using std::vector<any>::rbegin; - using std::vector<any>::rend; - using std::vector<any>::operator[]; - using std::vector<any>::at; - using std::vector<any>::resize; - using std::vector<any>::front; - using std::vector<any>::back; - using std::vector<any>::push_back; - using std::vector<any>::pop_back; - using std::vector<any>::insert; - using std::vector<any>::erase; - using std::vector<any>::clear; - using std::vector<any>::swap; - using std::vector<any>::emplace; - using std::vector<any>::emplace_back; + std::vector<unsigned int> tags; -private: - friend class Context; - friend class Sequence; - friend class PrioritizedChoice; - friend class Holder; + // Line number and column at which the matched string is + std::pair<size_t, size_t> line_info() const { + const auto &idx = *source_line_index; - const char* s_; - size_t n_; - size_t choice_count_; - size_t choice_; + auto cur = static_cast<size_t>(std::distance(ss, s_)); + auto it = std::lower_bound( + idx.begin(), idx.end(), cur, + [](size_t element, size_t value) { return element < value; }); - template <typename F> - auto transform(F f) const -> vector<typename std::remove_const<decltype(f(any()))>::type> { - vector<typename std::remove_const<decltype(f(any()))>::type> r; - for (const auto& v: *this) { - r.emplace_back(f(v)); - } - return r; - } + auto id = static_cast<size_t>(std::distance(idx.begin(), it)); + auto off = cur - (id == 0 ? 0 : idx[id - 1] + 1); + return std::make_pair(id + 1, off + 1); + } - template <typename F> - auto transform(size_t beg, size_t end, F f) const -> vector<typename std::remove_const<decltype(f(any()))>::type> { - vector<typename std::remove_const<decltype(f(any()))>::type> r; - end = (std::min)(end, size()); - for (size_t i = beg; i < end; i++) { - r.emplace_back(f((*this)[i])); - } - return r; + // Choice count + size_t choice_count() const { return choice_count_; } + + // Choice number (0 based index) + size_t choice() const { return choice_; } + + // Tokens + std::vector<std::pair<const char *, size_t>> tokens; + + std::string token(size_t id = 0) const { + if (!tokens.empty()) { + assert(id < tokens.size()); + const auto &tok = tokens[id]; + return std::string(tok.first, tok.second); } + return std::string(s_, n_); + } - void reset() { - path = nullptr; - ss = nullptr; - tokens.clear(); + // Transform the semantic value vector to another vector + template <typename T> + auto transform(size_t beg = 0, size_t end = static_cast<size_t>(-1)) const + -> vector<T> { + return this->transform(beg, end, + [](const any &v) { return any_cast<T>(v); }); + } + + using std::vector<any>::iterator; + using std::vector<any>::const_iterator; + using std::vector<any>::size; + using std::vector<any>::empty; + using std::vector<any>::assign; + using std::vector<any>::begin; + using std::vector<any>::end; + using std::vector<any>::rbegin; + using std::vector<any>::rend; + using std::vector<any>::operator[]; + using std::vector<any>::at; + using std::vector<any>::resize; + using std::vector<any>::front; + using std::vector<any>::back; + using std::vector<any>::push_back; + using std::vector<any>::pop_back; + using std::vector<any>::insert; + using std::vector<any>::erase; + using std::vector<any>::clear; + using std::vector<any>::swap; + using std::vector<any>::emplace; + using std::vector<any>::emplace_back; + +private: + friend class Context; + friend class Sequence; + friend class PrioritizedChoice; + friend class Holder; + friend class PrecedenceClimbing; + + const char *s_ = nullptr; + size_t n_ = 0; + size_t choice_count_ = 0; + size_t choice_ = 0; + std::string name_; + + template <typename F> + auto transform(F f) const + -> vector<typename std::remove_const<decltype(f(any()))>::type> { + vector<typename std::remove_const<decltype(f(any()))>::type> r; + for (const auto &v : *this) { + r.emplace_back(f(v)); + } + return r; + } - s_ = nullptr; - n_ = 0; - choice_count_ = 0; - choice_ = 0; + template <typename F> + auto transform(size_t beg, size_t end, F f) const + -> vector<typename std::remove_const<decltype(f(any()))>::type> { + vector<typename std::remove_const<decltype(f(any()))>::type> r; + end = (std::min)(end, size()); + for (size_t i = beg; i < end; i++) { + r.emplace_back(f((*this)[i])); } + return r; + } }; /* * Semantic action */ -template < - typename R, typename F, - typename std::enable_if<std::is_void<R>::value>::type*& = enabler, - typename... Args> -any call(F fn, Args&&... args) { - fn(std::forward<Args>(args)...); - return any(); -} - -template < - typename R, typename F, - typename std::enable_if<std::is_same<typename std::remove_cv<R>::type, any>::value>::type*& = enabler, - typename... Args> -any call(F fn, Args&&... args) { - return fn(std::forward<Args>(args)...); -} - -template < - typename R, typename F, - typename std::enable_if< - !std::is_void<R>::value && - !std::is_same<typename std::remove_cv<R>::type, any>::value>::type*& = enabler, - typename... Args> -any call(F fn, Args&&... args) { - return any(fn(std::forward<Args>(args)...)); -} - -class Action -{ +template <typename R, typename F, + typename std::enable_if<std::is_void<R>::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + fn(std::forward<Args>(args)...); + return any(); +} + +template <typename R, typename F, + typename std::enable_if< + std::is_same<typename std::remove_cv<R>::type, any>::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + return fn(std::forward<Args>(args)...); +} + +template <typename R, typename F, + typename std::enable_if< + !std::is_void<R>::value && + !std::is_same<typename std::remove_cv<R>::type, any>::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + return any(fn(std::forward<Args>(args)...)); +} + +class Action { public: - Action() = default; - - Action(const Action& rhs) : fn_(rhs.fn_) {} - - template <typename F, typename std::enable_if<!std::is_pointer<F>::value && !std::is_same<F, std::nullptr_t>::value>::type*& = enabler> - Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} - - template <typename F, typename std::enable_if<std::is_pointer<F>::value>::type*& = enabler> - Action(F fn) : fn_(make_adaptor(fn, fn)) {} - - template <typename F, typename std::enable_if<std::is_same<F, std::nullptr_t>::value>::type*& = enabler> - Action(F /*fn*/) {} - - template <typename F, typename std::enable_if<!std::is_pointer<F>::value && !std::is_same<F, std::nullptr_t>::value>::type*& = enabler> - void operator=(F fn) { - fn_ = make_adaptor(fn, &F::operator()); - } + Action() = default; + Action(const Action &rhs) = default; + + template <typename F, + typename std::enable_if<!std::is_pointer<F>::value && + !std::is_same<F, std::nullptr_t>::value, + std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} + + template <typename F, typename std::enable_if<std::is_pointer<F>::value, + std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, fn)) {} + + template <typename F, + typename std::enable_if<std::is_same<F, std::nullptr_t>::value, + std::nullptr_t>::type = nullptr> + Action(F /*fn*/) {} + + template <typename F, + typename std::enable_if<!std::is_pointer<F>::value && + !std::is_same<F, std::nullptr_t>::value, + std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, &F::operator()); + } - template <typename F, typename std::enable_if<std::is_pointer<F>::value>::type*& = enabler> - void operator=(F fn) { - fn_ = make_adaptor(fn, fn); - } + template <typename F, typename std::enable_if<std::is_pointer<F>::value, + std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, fn); + } - template <typename F, typename std::enable_if<std::is_same<F, std::nullptr_t>::value>::type*& = enabler> - void operator=(F /*fn*/) {} + template <typename F, + typename std::enable_if<std::is_same<F, std::nullptr_t>::value, + std::nullptr_t>::type = nullptr> + void operator=(F /*fn*/) {} - Action& operator=(const Action& rhs) = default; + Action &operator=(const Action &rhs) = default; - operator bool() const { - return bool(fn_); - } + operator bool() const { return bool(fn_); } - any operator()(SemanticValues& sv, any& dt) const { - return fn_(sv, dt); - } + any operator()(SemanticValues &sv, any &dt) const { return fn_(sv, dt); } private: - template <typename R> - struct TypeAdaptor_sv { - TypeAdaptor_sv(std::function<R (SemanticValues& sv)> fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call<R>(fn_, sv); - } - std::function<R (SemanticValues& sv)> fn_; - }; - - template <typename R> - struct TypeAdaptor_csv { - TypeAdaptor_csv(std::function<R (const SemanticValues& sv)> fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call<R>(fn_, sv); - } - std::function<R (const SemanticValues& sv)> fn_; - }; - - template <typename R> - struct TypeAdaptor_sv_dt { - TypeAdaptor_sv_dt(std::function<R (SemanticValues& sv, any& dt)> fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call<R>(fn_, sv, dt); - } - std::function<R (SemanticValues& sv, any& dt)> fn_; - }; - - template <typename R> - struct TypeAdaptor_csv_dt { - TypeAdaptor_csv_dt(std::function<R (const SemanticValues& sv, any& dt)> fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call<R>(fn_, sv, dt); - } - std::function<R (const SemanticValues& sv, any& dt)> fn_; - }; - - typedef std::function<any (SemanticValues& sv, any& dt)> Fty; - - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv) const) { - return TypeAdaptor_sv<R>(fn); - } + template <typename R> struct TypeAdaptor_sv { + TypeAdaptor_sv(std::function<R(SemanticValues &sv)> fn) : fn_(fn) {} + any operator()(SemanticValues &sv, any & /*dt*/) { + return call<R>(fn_, sv); + } + std::function<R(SemanticValues &sv)> fn_; + }; + + template <typename R> struct TypeAdaptor_csv { + TypeAdaptor_csv(std::function<R(const SemanticValues &sv)> fn) : fn_(fn) {} + any operator()(SemanticValues &sv, any & /*dt*/) { + return call<R>(fn_, sv); + } + std::function<R(const SemanticValues &sv)> fn_; + }; + + template <typename R> struct TypeAdaptor_sv_dt { + TypeAdaptor_sv_dt(std::function<R(SemanticValues &sv, any &dt)> fn) + : fn_(fn) {} + any operator()(SemanticValues &sv, any &dt) { return call<R>(fn_, sv, dt); } + std::function<R(SemanticValues &sv, any &dt)> fn_; + }; + + template <typename R> struct TypeAdaptor_csv_dt { + TypeAdaptor_csv_dt(std::function<R(const SemanticValues &sv, any &dt)> fn) + : fn_(fn) {} + any operator()(SemanticValues &sv, any &dt) { return call<R>(fn_, sv, dt); } + std::function<R(const SemanticValues &sv, any &dt)> fn_; + }; + + typedef std::function<any(SemanticValues &sv, any &dt)> Fty; + + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv) const) { + return TypeAdaptor_sv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv) const) { - return TypeAdaptor_csv<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv) const) { + return TypeAdaptor_csv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv)) { + return TypeAdaptor_sv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv)) { + return TypeAdaptor_csv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (*)(SemanticValues &sv)) { + return TypeAdaptor_sv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (*)(const SemanticValues &sv)) { + return TypeAdaptor_csv<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt) const) { - return TypeAdaptor_sv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv, any &dt) const) { + return TypeAdaptor_sv_dt<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt) const) { - return TypeAdaptor_csv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv, any &dt) const) { + return TypeAdaptor_csv_dt<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv, any &dt)) { + return TypeAdaptor_sv_dt<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv, any &dt)) { + return TypeAdaptor_csv_dt<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R(* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (*)(SemanticValues &sv, any &dt)) { + return TypeAdaptor_sv_dt<R>(fn); + } - template<typename F, typename R> - Fty make_adaptor(F fn, R(* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt<R>(fn); - } + template <typename F, typename R> + Fty make_adaptor(F fn, R (*)(const SemanticValues &sv, any &dt)) { + return TypeAdaptor_csv_dt<R>(fn); + } - Fty fn_; + Fty fn_; }; /* * Semantic predicate */ -// Note: 'parse_error' exception class should be be used in sematic action handlers to reject the rule. +// Note: 'parse_error' exception class should be be used in sematic action +// handlers to reject the rule. struct parse_error { - parse_error() = default; - parse_error(const char* s) : s_(s) {} - const char* what() const { return s_.empty() ? nullptr : s_.c_str(); } + parse_error() = default; + parse_error(const char *s) : s_(s) {} + const char *what() const { return s_.empty() ? nullptr : s_.c_str(); } + private: - std::string s_; + std::string s_; }; /* * Result */ -inline bool success(size_t len) { - return len != static_cast<size_t>(-1); -} +inline bool success(size_t len) { return len != static_cast<size_t>(-1); } -inline bool fail(size_t len) { - return len == static_cast<size_t>(-1); -} +inline bool fail(size_t len) { return len == static_cast<size_t>(-1); } /* * Context @@ -1220,2317 +1296,2833 @@ class Context; class Ope; class Definition; -typedef std::function<void (const char* name, const char* s, size_t n, const SemanticValues& sv, const Context& c, const any& dt)> Tracer; +typedef std::function<void(const char *name, const char *s, size_t n, + const SemanticValues &sv, const Context &c, + const any &dt)> + TracerEnter; -class Context -{ +typedef std::function<void(const char *name, const char *s, size_t n, + const SemanticValues &sv, const Context &c, + const any &dt, size_t)> + TracerLeave; + +class Context { public: - const char* path; - const char* s; - const size_t l; - - const char* error_pos; - const char* message_pos; - std::string message; // TODO: should be `int`. - - std::vector<std::shared_ptr<SemanticValues>> value_stack; - size_t value_stack_size; - std::vector<std::vector<std::shared_ptr<Ope>>> args_stack; - - size_t nest_level; - - bool in_token; - - std::shared_ptr<Ope> whitespaceOpe; - bool in_whitespace; - - std::shared_ptr<Ope> wordOpe; - - std::vector<std::unordered_map<std::string, std::string>> capture_scope_stack; - - const size_t def_count; - const bool enablePackratParsing; - std::vector<bool> cache_registered; - std::vector<bool> cache_success; - - std::map<std::pair<size_t, size_t>, std::tuple<size_t, any>> cache_values; - - std::function<void (const char*, const char*, size_t, const SemanticValues&, const Context&, const any&)> tracer; - - Context( - const char* a_path, - const char* a_s, - size_t a_l, - size_t a_def_count, - std::shared_ptr<Ope> a_whitespaceOpe, - std::shared_ptr<Ope> a_wordOpe, - bool a_enablePackratParsing, - Tracer a_tracer) - : path(a_path) - , s(a_s) - , l(a_l) - , error_pos(nullptr) - , message_pos(nullptr) - , value_stack_size(0) - , nest_level(0) - , in_token(false) - , whitespaceOpe(a_whitespaceOpe) - , in_whitespace(false) - , wordOpe(a_wordOpe) - , def_count(a_def_count) - , enablePackratParsing(a_enablePackratParsing) - , cache_registered(enablePackratParsing ? def_count * (l + 1) : 0) - , cache_success(enablePackratParsing ? def_count * (l + 1) : 0) - , tracer(a_tracer) - { - args_stack.resize(1); - capture_scope_stack.resize(1); - } + const char *path; + const char *s; + const size_t l; + std::vector<size_t> source_line_index; - template <typename T> - void packrat(const char* a_s, size_t def_id, size_t& len, any& val, T fn) { - if (!enablePackratParsing) { - fn(val); - return; - } + const char *error_pos = nullptr; + const char *message_pos = nullptr; + std::string message; // TODO: should be `int`. - auto col = a_s - s; - auto idx = def_count * static_cast<size_t>(col) + def_id; + std::vector<std::shared_ptr<SemanticValues>> value_stack; + size_t value_stack_size = 0; + std::vector<std::vector<std::shared_ptr<Ope>>> args_stack; - if (cache_registered[idx]) { - if (cache_success[idx]) { - auto key = std::make_pair(col, def_id); - std::tie(len, val) = cache_values[key]; - return; - } else { - len = static_cast<size_t>(-1); - return; - } - } else { - fn(val); - cache_registered[idx] = true; - cache_success[idx] = success(len); - if (success(len)) { - auto key = std::make_pair(col, def_id); - cache_values[key] = std::make_pair(len, val); - } - return; - } - } + bool in_token = false; - SemanticValues& push() { - assert(value_stack_size <= value_stack.size()); - if (value_stack_size == value_stack.size()) { - value_stack.emplace_back(std::make_shared<SemanticValues>()); - } - auto& sv = *value_stack[value_stack_size++]; - if (!sv.empty()) { - sv.clear(); - } - sv.reset(); - sv.path = path; - sv.ss = s; - return sv; - } + std::shared_ptr<Ope> whitespaceOpe; + bool in_whitespace = false; - void pop() { - value_stack_size--; - } + std::shared_ptr<Ope> wordOpe; - void push_args(const std::vector<std::shared_ptr<Ope>>& args) { - args_stack.push_back(args); - } + std::vector<std::map<std::string, std::string>> capture_scope_stack; + size_t capture_scope_stack_size = 0; - void pop_args() { - args_stack.pop_back(); - } + const size_t def_count; + const bool enablePackratParsing; + std::vector<bool> cache_registered; + std::vector<bool> cache_success; - const std::vector<std::shared_ptr<Ope>>& top_args() const { - return args_stack[args_stack.size() - 1]; - } + std::map<std::pair<size_t, size_t>, std::tuple<size_t, any>> cache_values; + + TracerEnter tracer_enter; + TracerLeave tracer_leave; + + Context(const char *a_path, const char *a_s, size_t a_l, size_t a_def_count, + std::shared_ptr<Ope> a_whitespaceOpe, std::shared_ptr<Ope> a_wordOpe, + bool a_enablePackratParsing, TracerEnter a_tracer_enter, + TracerLeave a_tracer_leave) + : path(a_path), s(a_s), l(a_l), whitespaceOpe(a_whitespaceOpe), + wordOpe(a_wordOpe), def_count(a_def_count), + enablePackratParsing(a_enablePackratParsing), + cache_registered(enablePackratParsing ? def_count * (l + 1) : 0), + cache_success(enablePackratParsing ? def_count * (l + 1) : 0), + tracer_enter(a_tracer_enter), tracer_leave(a_tracer_leave) { - void push_capture_scope() { - capture_scope_stack.resize(capture_scope_stack.size() + 1); + for (size_t pos = 0; pos < l; pos++) { + if (s[pos] == '\n') { source_line_index.push_back(pos); } } + source_line_index.push_back(l); + + args_stack.resize(1); + + push_capture_scope(); + } + + ~Context() { assert(!value_stack_size); } - void pop_capture_scope() { - capture_scope_stack.pop_back(); + Context(const Context &) = delete; + Context(Context &&) = delete; + Context operator=(const Context &) = delete; + + template <typename T> + void packrat(const char *a_s, size_t def_id, size_t &len, any &val, T fn) { + if (!enablePackratParsing) { + fn(val); + return; } - void shift_capture_values() { - assert(capture_scope_stack.size() >= 2); - auto it = capture_scope_stack.rbegin(); - auto it_prev = it + 1; - for (const auto& kv: *it) { - (*it_prev)[kv.first] = kv.second; - } + auto col = a_s - s; + auto idx = def_count * static_cast<size_t>(col) + def_id; + + if (cache_registered[idx]) { + if (cache_success[idx]) { + auto key = std::make_pair(col, def_id); + std::tie(len, val) = cache_values[key]; + return; + } else { + len = static_cast<size_t>(-1); + return; + } + } else { + fn(val); + cache_registered[idx] = true; + cache_success[idx] = success(len); + if (success(len)) { + auto key = std::make_pair(col, def_id); + cache_values[key] = std::make_pair(len, val); + } + return; } + } + + SemanticValues &push() { + assert(value_stack_size <= value_stack.size()); + if (value_stack_size == value_stack.size()) { + value_stack.emplace_back(std::make_shared<SemanticValues>()); + } else { + auto &sv = *value_stack[value_stack_size]; + if (!sv.empty()) { + sv.clear(); + sv.tags.clear(); + } + sv.s_ = nullptr; + sv.n_ = 0; + sv.choice_count_ = 0; + sv.choice_ = 0; + sv.tokens.clear(); + } + + auto &sv = *value_stack[value_stack_size++]; + sv.path = path; + sv.ss = s; + sv.source_line_index = &source_line_index; + return sv; + } + + void pop() { value_stack_size--; } + + void push_args(std::vector<std::shared_ptr<Ope>> &&args) { + args_stack.emplace_back(args); + } - void set_error_pos(const char* a_s) { - if (error_pos < a_s) error_pos = a_s; + void pop_args() { args_stack.pop_back(); } + + const std::vector<std::shared_ptr<Ope>> &top_args() const { + return args_stack[args_stack.size() - 1]; + } + + void push_capture_scope() { + assert(capture_scope_stack_size <= capture_scope_stack.size()); + if (capture_scope_stack_size == capture_scope_stack.size()) { + capture_scope_stack.emplace_back(std::map<std::string, std::string>()); + } else { + auto &cs = capture_scope_stack[capture_scope_stack_size]; + cs.clear(); } + capture_scope_stack_size++; + } + + void pop_capture_scope() { capture_scope_stack_size--; } - void trace(const char* name, const char* a_s, size_t n, SemanticValues& sv, any& dt) const { - if (tracer) tracer(name, a_s, n, sv, *this, dt); + void shift_capture_values() { + assert(capture_scope_stack.size() >= 2); + auto curr = &capture_scope_stack[capture_scope_stack_size - 1]; + auto prev = curr - 1; + for (const auto &kv : *curr) { + (*prev)[kv.first] = kv.second; } + } + + void set_error_pos(const char *a_s) { + if (error_pos < a_s) error_pos = a_s; + } + + void trace_enter(const char *name, const char *a_s, size_t n, + SemanticValues &sv, any &dt) const; + void trace_leave(const char *name, const char *a_s, size_t n, + SemanticValues &sv, any &dt, size_t len) const; + bool is_traceable(const Ope &ope) const; + + mutable size_t next_trace_id = 0; + mutable std::list<size_t> trace_ids; }; /* * Parser operators */ -class Ope -{ +class Ope { public: - struct Visitor; - - virtual ~Ope() {} - virtual size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const = 0; - virtual void accept(Visitor& v) = 0; + struct Visitor; + + virtual ~Ope() {} + size_t parse(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const; + virtual size_t parse_core(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt) const = 0; + virtual void accept(Visitor &v) = 0; }; -class Sequence : public Ope -{ +class Sequence : public Ope { public: - Sequence(const Sequence& rhs) : opes_(rhs.opes_) {} - -#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 - // NOTE: Compiler Error C2797 on Visual Studio 2013 - // "The C++ compiler in Visual Studio does not implement list - // initialization inside either a member initializer list or a non-static - // data member initializer. Before Visual Studio 2013 Update 3, this was - // silently converted to a function call, which could lead to bad code - // generation. Visual Studio 2013 Update 3 reports this as an error." - template <typename... Args> - Sequence(const Args& ...args) { - opes_ = std::vector<std::shared_ptr<Ope>>{ static_cast<std::shared_ptr<Ope>>(args)... }; + template <typename... Args> + Sequence(const Args &... args) + : opes_{static_cast<std::shared_ptr<Ope>>(args)...} {} + Sequence(const std::vector<std::shared_ptr<Ope>> &opes) : opes_(opes) {} + Sequence(std::vector<std::shared_ptr<Ope>> &&opes) : opes_(opes) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + auto &chldsv = c.push(); + auto pop_se = make_scope_exit([&]() { c.pop(); }); + size_t i = 0; + for (const auto &ope : opes_) { + const auto &rule = *ope; + auto len = rule.parse(s + i, n - i, chldsv, c, dt); + if (fail(len)) { return static_cast<size_t>(-1); } + i += len; + } + if (!chldsv.empty()) { + for (size_t j = 0; j < chldsv.size(); j++) { + sv.emplace_back(std::move(chldsv[j])); + } } -#else - template <typename... Args> - Sequence(const Args& ...args) : opes_{ static_cast<std::shared_ptr<Ope>>(args)... } {} -#endif - - Sequence(const std::vector<std::shared_ptr<Ope>>& opes) : opes_(opes) {} - Sequence(std::vector<std::shared_ptr<Ope>>&& opes) : opes_(opes) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Sequence", s, n, sv, dt); - auto& chldsv = c.push(); - size_t i = 0; - for (const auto& ope : opes_) { - c.nest_level++; - auto se = make_scope_exit([&]() { c.nest_level--; }); - const auto& rule = *ope; - auto len = rule.parse(s + i, n - i, chldsv, c, dt); - if (fail(len)) { - return static_cast<size_t>(-1); - } - i += len; - } - sv.insert(sv.end(), chldsv.begin(), chldsv.end()); - sv.s_ = chldsv.c_str(); - sv.n_ = chldsv.length(); - sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); - return i; + if (!chldsv.tags.empty()) { + for (size_t j = 0; j < chldsv.tags.size(); j++) { + sv.tags.emplace_back(std::move(chldsv.tags[j])); + } + } + sv.s_ = chldsv.c_str(); + sv.n_ = chldsv.length(); + if (!chldsv.tokens.empty()) { + for (size_t j = 0; j < chldsv.tokens.size(); j++) { + sv.tokens.emplace_back(std::move(chldsv.tokens[j])); + } } + return i; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::vector<std::shared_ptr<Ope>> opes_; + std::vector<std::shared_ptr<Ope>> opes_; }; -class PrioritizedChoice : public Ope -{ +class PrioritizedChoice : public Ope { public: -#if defined(_MSC_VER) && _MSC_VER < 1900 // Less than Visual Studio 2015 - // NOTE: Compiler Error C2797 on Visual Studio 2013 - // "The C++ compiler in Visual Studio does not implement list - // initialization inside either a member initializer list or a non-static - // data member initializer. Before Visual Studio 2013 Update 3, this was - // silently converted to a function call, which could lead to bad code - // generation. Visual Studio 2013 Update 3 reports this as an error." - template <typename... Args> - PrioritizedChoice(const Args& ...args) { - opes_ = std::vector<std::shared_ptr<Ope>>{ static_cast<std::shared_ptr<Ope>>(args)... }; - } -#else - template <typename... Args> - PrioritizedChoice(const Args& ...args) : opes_{ static_cast<std::shared_ptr<Ope>>(args)... } {} -#endif - - PrioritizedChoice(const std::vector<std::shared_ptr<Ope>>& opes) : opes_(opes) {} - PrioritizedChoice(std::vector<std::shared_ptr<Ope>>&& opes) : opes_(opes) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("PrioritizedChoice", s, n, sv, dt); - size_t id = 0; - for (const auto& ope : opes_) { - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope; - auto len = rule.parse(s, n, chldsv, c, dt); - if (success(len)) { - sv.insert(sv.end(), chldsv.begin(), chldsv.end()); - sv.s_ = chldsv.c_str(); - sv.n_ = chldsv.length(); - sv.choice_count_ = opes_.size(); - sv.choice_ = id; - sv.tokens.insert(sv.tokens.end(), chldsv.tokens.begin(), chldsv.tokens.end()); - - c.shift_capture_values(); - return len; - } - id++; + template <typename... Args> + PrioritizedChoice(const Args &... args) + : opes_{static_cast<std::shared_ptr<Ope>>(args)...} {} + PrioritizedChoice(const std::vector<std::shared_ptr<Ope>> &opes) + : opes_(opes) {} + PrioritizedChoice(std::vector<std::shared_ptr<Ope>> &&opes) : opes_(opes) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + size_t id = 0; + for (const auto &ope : opes_) { + auto &chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.pop(); + c.pop_capture_scope(); + }); + const auto &rule = *ope; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + if (!chldsv.empty()) { + for (size_t i = 0; i < chldsv.size(); i++) { + sv.emplace_back(std::move(chldsv[i])); + } + } + if (!chldsv.tags.empty()) { + for (size_t i = 0; i < chldsv.tags.size(); i++) { + sv.tags.emplace_back(std::move(chldsv.tags[i])); + } } - return static_cast<size_t>(-1); + sv.s_ = chldsv.c_str(); + sv.n_ = chldsv.length(); + sv.choice_count_ = opes_.size(); + sv.choice_ = id; + if (!chldsv.tokens.empty()) { + for (size_t i = 0; i < chldsv.tokens.size(); i++) { + sv.tokens.emplace_back(std::move(chldsv.tokens[i])); + } + } + + c.shift_capture_values(); + return len; + } + id++; } + return static_cast<size_t>(-1); + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - size_t size() const { return opes_.size(); } + size_t size() const { return opes_.size(); } - std::vector<std::shared_ptr<Ope>> opes_; + std::vector<std::shared_ptr<Ope>> opes_; }; -class ZeroOrMore : public Ope -{ +class Repetition : public Ope { public: - ZeroOrMore(const std::shared_ptr<Ope>& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("ZeroOrMore", s, n, sv, dt); - auto save_error_pos = c.error_pos; - size_t i = 0; - while (n - i > 0) { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - const auto& rule = *ope_; - auto len = rule.parse(s + i, n - i, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast<std::ptrdiff_t>(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast<std::ptrdiff_t>(save_tok_size)); - } - c.error_pos = save_error_pos; - break; - } - i += len; - } - return i; + Repetition(const std::shared_ptr<Ope> &ope, size_t min, size_t max) + : ope_(ope), min_(min), max_(max) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + size_t count = 0; + size_t i = 0; + while (count < min_) { + c.push_capture_scope(); + auto se = make_scope_exit([&]() { c.pop_capture_scope(); }); + const auto &rule = *ope_; + auto len = rule.parse(s + i, n - i, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + } else { + return static_cast<size_t>(-1); + } + i += len; + count++; + } + + auto save_error_pos = c.error_pos; + while (n - i > 0 && count < max_) { + c.push_capture_scope(); + auto se = make_scope_exit([&]() { c.pop_capture_scope(); }); + auto save_sv_size = sv.size(); + auto save_tok_size = sv.tokens.size(); + const auto &rule = *ope_; + auto len = rule.parse(s + i, n - i, sv, c, dt); + if (success(len)) { + c.shift_capture_values(); + } else { + if (sv.size() != save_sv_size) { + sv.erase(sv.begin() + static_cast<std::ptrdiff_t>(save_sv_size)); + sv.tags.erase(sv.tags.begin() + + static_cast<std::ptrdiff_t>(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + + static_cast<std::ptrdiff_t>(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + count++; } + return i; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; -}; + bool is_zom() const { + return min_ == 0 && max_ == std::numeric_limits<size_t>::max(); + } -class OneOrMore : public Ope -{ -public: - OneOrMore(const std::shared_ptr<Ope>& ope) : ope_(ope) {} + static std::shared_ptr<Repetition> zom(const std::shared_ptr<Ope> &ope) { + return std::make_shared<Repetition>(ope, 0, + std::numeric_limits<size_t>::max()); + } - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("OneOrMore", s, n, sv, dt); - size_t len = 0; - { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - len = rule.parse(s, n, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - } else { - return static_cast<size_t>(-1); - } - } - auto save_error_pos = c.error_pos; - auto i = len; - while (n - i > 0) { - c.nest_level++; - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - const auto& rule = *ope_; - len = rule.parse(s + i, n - i, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast<std::ptrdiff_t>(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast<std::ptrdiff_t>(save_tok_size)); - } - c.error_pos = save_error_pos; - break; - } - i += len; - } - return i; - } + static std::shared_ptr<Repetition> oom(const std::shared_ptr<Ope> &ope) { + return std::make_shared<Repetition>(ope, 1, + std::numeric_limits<size_t>::max()); + } - void accept(Visitor& v) override; + static std::shared_ptr<Repetition> opt(const std::shared_ptr<Ope> &ope) { + return std::make_shared<Repetition>(ope, 0, 1); + } - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; + size_t min_; + size_t max_; }; -class Option : public Ope -{ +class AndPredicate : public Ope { public: - Option(const std::shared_ptr<Ope>& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Option", s, n, sv, dt); - auto save_error_pos = c.error_pos; - c.nest_level++; - auto save_sv_size = sv.size(); - auto save_tok_size = sv.tokens.size(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - if (success(len)) { - c.shift_capture_values(); - return len; - } else { - if (sv.size() != save_sv_size) { - sv.erase(sv.begin() + static_cast<std::ptrdiff_t>(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast<std::ptrdiff_t>(save_tok_size)); - } - c.error_pos = save_error_pos; - return 0; - } + AndPredicate(const std::shared_ptr<Ope> &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any &dt) const override { + auto &chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.pop(); + c.pop_capture_scope(); + }); + const auto &rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + return 0; + } else { + return static_cast<size_t>(-1); } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; }; -class AndPredicate : public Ope -{ +class NotPredicate : public Ope { public: - AndPredicate(const std::shared_ptr<Ope>& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("AndPredicate", s, n, sv, dt); - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, chldsv, c, dt); - if (success(len)) { - return 0; - } else { - return static_cast<size_t>(-1); - } + NotPredicate(const std::shared_ptr<Ope> &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any &dt) const override { + auto save_error_pos = c.error_pos; + auto &chldsv = c.push(); + c.push_capture_scope(); + auto se = make_scope_exit([&]() { + c.pop(); + c.pop_capture_scope(); + }); + const auto &rule = *ope_; + auto len = rule.parse(s, n, chldsv, c, dt); + if (success(len)) { + c.set_error_pos(s); + return static_cast<size_t>(-1); + } else { + c.error_pos = save_error_pos; + return 0; } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; }; -class NotPredicate : public Ope -{ +class Dictionary : public Ope, public std::enable_shared_from_this<Dictionary> { public: - NotPredicate(const std::shared_ptr<Ope>& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("NotPredicate", s, n, sv, dt); - auto save_error_pos = c.error_pos; - c.nest_level++; - auto& chldsv = c.push(); - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.nest_level--; - c.pop(); - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, chldsv, c, dt); - if (success(len)) { - c.set_error_pos(s); - return static_cast<size_t>(-1); - } else { - c.error_pos = save_error_pos; - return 0; - } - } + Dictionary(const std::vector<std::string> &v) : trie_(v) {} - void accept(Visitor& v) override; + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - std::shared_ptr<Ope> ope_; + void accept(Visitor &v) override; + + Trie trie_; }; -class LiteralString : public Ope - , public std::enable_shared_from_this<LiteralString> -{ +class LiteralString : public Ope, + public std::enable_shared_from_this<LiteralString> { public: - LiteralString(const std::string& s) - : lit_(s) - , init_is_word_(false) - , is_word_(false) - {} + LiteralString(const std::string &s, bool ignore_case) + : lit_(s), ignore_case_(ignore_case), init_is_word_(false), + is_word_(false) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::string lit_; - mutable bool init_is_word_; - mutable bool is_word_; + std::string lit_; + bool ignore_case_; + mutable bool init_is_word_; + mutable bool is_word_; }; -class CharacterClass : public Ope - , public std::enable_shared_from_this<CharacterClass> -{ +class CharacterClass : public Ope, + public std::enable_shared_from_this<CharacterClass> { public: - CharacterClass(const std::string& s) { - auto chars = decode(s.c_str(), s.length()); - auto i = 0u; - while (i < chars.size()) { - if (i + 2 < chars.size() && chars[i + 1] == '-') { - auto cp1 = chars[i]; - auto cp2 = chars[i + 2]; - ranges_.emplace_back(std::make_pair(cp1, cp2)); - i += 3; - } else { - auto cp = chars[i]; - ranges_.emplace_back(std::make_pair(cp, cp)); - i += 1; - } - } + CharacterClass(const std::string &s, bool negated) : negated_(negated) { + auto chars = decode(s.c_str(), s.length()); + auto i = 0u; + while (i < chars.size()) { + if (i + 2 < chars.size() && chars[i + 1] == '-') { + auto cp1 = chars[i]; + auto cp2 = chars[i + 2]; + ranges_.emplace_back(std::make_pair(cp1, cp2)); + i += 3; + } else { + auto cp = chars[i]; + ranges_.emplace_back(std::make_pair(cp, cp)); + i += 1; + } } + assert(!ranges_.empty()); + } - CharacterClass(const std::vector<std::pair<char32_t, char32_t>>& ranges) : ranges_(ranges) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("CharacterClass", s, n, sv, dt); + CharacterClass(const std::vector<std::pair<char32_t, char32_t>> &ranges, + bool negated) + : ranges_(ranges), negated_(negated) { + assert(!ranges_.empty()); + } - if (n < 1) { - c.set_error_pos(s); - return static_cast<size_t>(-1); - } + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any & /*dt*/) const override { + if (n < 1) { + c.set_error_pos(s); + return static_cast<size_t>(-1); + } - char32_t cp; - auto len = decode_codepoint(s, n, cp); + char32_t cp = 0; + auto len = decode_codepoint(s, n, cp); - if (!ranges_.empty()) { - for (const auto& range: ranges_) { - if (range.first <= cp && cp <= range.second) { - return len; - } - } + for (const auto &range : ranges_) { + if (range.first <= cp && cp <= range.second) { + if (negated_) { + c.set_error_pos(s); + return static_cast<size_t>(-1); + } else { + return len; } + } + } - c.set_error_pos(s); - return static_cast<size_t>(-1); + if (negated_) { + return len; + } else { + c.set_error_pos(s); + return static_cast<size_t>(-1); } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::vector<std::pair<char32_t, char32_t>> ranges_; + std::vector<std::pair<char32_t, char32_t>> ranges_; + bool negated_; }; -class Character : public Ope - , public std::enable_shared_from_this<Character> -{ +class Character : public Ope, public std::enable_shared_from_this<Character> { public: - Character(char ch) : ch_(ch) {} + Character(char ch) : ch_(ch) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("Character", s, n, sv, dt); - if (n < 1 || s[0] != ch_) { - c.set_error_pos(s); - return static_cast<size_t>(-1); - } - return 1; + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any & /*dt*/) const override { + if (n < 1 || s[0] != ch_) { + c.set_error_pos(s); + return static_cast<size_t>(-1); } + return 1; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - char ch_; + char ch_; }; -class AnyCharacter : public Ope - , public std::enable_shared_from_this<AnyCharacter> -{ +class AnyCharacter : public Ope, + public std::enable_shared_from_this<AnyCharacter> { public: - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("AnyCharacter", s, n, sv, dt); - auto len = codepoint_length(s, n); - if (len < 1) { - c.set_error_pos(s); - return static_cast<size_t>(-1); - } - return len; + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any & /*dt*/) const override { + auto len = codepoint_length(s, n); + if (len < 1) { + c.set_error_pos(s); + return static_cast<size_t>(-1); } + return len; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; }; -class CaptureScope : public Ope -{ +class CaptureScope : public Ope { public: - CaptureScope(const std::shared_ptr<Ope>& ope) - : ope_(ope) {} + CaptureScope(const std::shared_ptr<Ope> &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.push_capture_scope(); - auto se = make_scope_exit([&]() { - c.pop_capture_scope(); - }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - return len; - } + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + c.push_capture_scope(); + auto se = make_scope_exit([&]() { c.pop_capture_scope(); }); + const auto &rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + return len; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; }; -class Capture : public Ope -{ +class Capture : public Ope { public: - typedef std::function<void (const char* s, size_t n, Context& c)> MatchAction; + typedef std::function<void(const char *s, size_t n, Context &c)> MatchAction; - Capture(const std::shared_ptr<Ope>& ope, MatchAction ma) - : ope_(ope), match_action_(ma) {} + Capture(const std::shared_ptr<Ope> &ope, MatchAction ma) + : ope_(ope), match_action_(ma) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - if (success(len) && match_action_) { - match_action_(s, len, c); - } - return len; - } + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + const auto &rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len) && match_action_) { match_action_(s, len, c); } + return len; + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; - MatchAction match_action_; + std::shared_ptr<Ope> ope_; + MatchAction match_action_; }; -class TokenBoundary : public Ope -{ +class TokenBoundary : public Ope { public: - TokenBoundary(const std::shared_ptr<Ope>& ope) : ope_(ope) {} + TokenBoundary(const std::shared_ptr<Ope> &ope) : ope_(ope) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; }; -class Ignore : public Ope -{ +class Ignore : public Ope { public: - Ignore(const std::shared_ptr<Ope>& ope) : ope_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& /*sv*/, Context& c, any& dt) const override { - const auto& rule = *ope_; - auto& chldsv = c.push(); - auto se = make_scope_exit([&]() { - c.pop(); - }); - return rule.parse(s, n, chldsv, c, dt); - } + Ignore(const std::shared_ptr<Ope> &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues & /*sv*/, + Context &c, any &dt) const override { + const auto &rule = *ope_; + auto &chldsv = c.push(); + auto se = make_scope_exit([&]() { c.pop(); }); + return rule.parse(s, n, chldsv, c, dt); + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::shared_ptr<Ope> ope_; }; -typedef std::function<size_t (const char* s, size_t n, SemanticValues& sv, any& dt)> Parser; +typedef std::function<size_t(const char *s, size_t n, SemanticValues &sv, + any &dt)> + Parser; -class User : public Ope -{ +class User : public Ope { public: - User(Parser fn) : fn_(fn) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("User", s, n, sv, dt); - assert(fn_); - return fn_(s, n, sv, dt); - } - void accept(Visitor& v) override; - std::function<size_t (const char* s, size_t n, SemanticValues& sv, any& dt)> fn_; + User(Parser fn) : fn_(fn) {} + size_t parse_core(const char *s, size_t n, SemanticValues &sv, + Context & /*c*/, any &dt) const override { + assert(fn_); + return fn_(s, n, sv, dt); + } + void accept(Visitor &v) override; + std::function<size_t(const char *s, size_t n, SemanticValues &sv, any &dt)> + fn_; }; -class WeakHolder : public Ope -{ +class WeakHolder : public Ope { public: - WeakHolder(const std::shared_ptr<Ope>& ope) : weak_(ope) {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - auto ope = weak_.lock(); - assert(ope); - const auto& rule = *ope; - return rule.parse(s, n, sv, c, dt); - } + WeakHolder(const std::shared_ptr<Ope> &ope) : weak_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + auto ope = weak_.lock(); + assert(ope); + const auto &rule = *ope; + return rule.parse(s, n, sv, c, dt); + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::weak_ptr<Ope> weak_; + std::weak_ptr<Ope> weak_; }; -class Holder : public Ope -{ +class Holder : public Ope { public: - Holder(Definition* outer) - : outer_(outer) {} + Holder(Definition *outer) : outer_(outer) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; - any reduce(SemanticValues& sv, any& dt) const; + any reduce(SemanticValues &sv, any &dt) const; - std::shared_ptr<Ope> ope_; - Definition* outer_; + const char *trace_name() const; - friend class Definition; + std::shared_ptr<Ope> ope_; + Definition *outer_; + mutable std::string trace_name_; + + friend class Definition; }; typedef std::unordered_map<std::string, Definition> Grammar; -class Reference : public Ope - , public std::enable_shared_from_this<Reference> -{ +class Reference : public Ope, public std::enable_shared_from_this<Reference> { public: - Reference( - const Grammar& grammar, - const std::string& name, - const char* s, - bool is_macro, - const std::vector<std::shared_ptr<Ope>>& args) - : grammar_(grammar) - , name_(name) - , s_(s) - , is_macro_(is_macro) - , args_(args) - , rule_(nullptr) - , iarg_(0) - {} - - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; - - void accept(Visitor& v) override; - - std::shared_ptr<Ope> get_core_operator() const; - - const Grammar& grammar_; - const std::string name_; - const char* s_; - - const bool is_macro_; - const std::vector<std::shared_ptr<Ope>> args_; - - Definition* rule_; - size_t iarg_; + Reference(const Grammar &grammar, const std::string &name, const char *s, + bool is_macro, const std::vector<std::shared_ptr<Ope>> &args) + : grammar_(grammar), name_(name), s_(s), is_macro_(is_macro), args_(args), + rule_(nullptr), iarg_(0) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; + + void accept(Visitor &v) override; + + std::shared_ptr<Ope> get_core_operator() const; + + const Grammar &grammar_; + const std::string name_; + const char *s_; + + const bool is_macro_; + const std::vector<std::shared_ptr<Ope>> args_; + + Definition *rule_; + size_t iarg_; }; -class Whitespace : public Ope -{ +class Whitespace : public Ope { +public: + Whitespace(const std::shared_ptr<Ope> &ope) : ope_(ope) {} + + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + if (c.in_whitespace) { return 0; } + c.in_whitespace = true; + auto se = make_scope_exit([&]() { c.in_whitespace = false; }); + const auto &rule = *ope_; + return rule.parse(s, n, sv, c, dt); + } + + void accept(Visitor &v) override; + + std::shared_ptr<Ope> ope_; +}; + +class BackReference : public Ope { public: - Whitespace(const std::shared_ptr<Ope>& ope) : ope_(ope) {} + BackReference(const std::string &name) : name_(name) {} - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - if (c.in_whitespace) { - return 0; - } - c.in_whitespace = true; - auto se = make_scope_exit([&]() { c.in_whitespace = false; }); - const auto& rule = *ope_; - return rule.parse(s, n, sv, c, dt); - } + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr<Ope> ope_; + std::string name_; }; -class BackReference : public Ope -{ +class PrecedenceClimbing : public Ope { public: - BackReference(const std::string& name) : name_(name) {} + using BinOpeInfo = std::map<std::string, std::pair<size_t, char>>; - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + PrecedenceClimbing(const std::shared_ptr<Ope> &atom, + const std::shared_ptr<Ope> &binop, const BinOpeInfo &info, + const Definition &rule) + : atom_(atom), binop_(binop), info_(info), rule_(rule) {} - void accept(Visitor& v) override; + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override { + return parse_expression(s, n, sv, c, dt, 0); + } + + void accept(Visitor &v) override; + + std::shared_ptr<Ope> atom_; + std::shared_ptr<Ope> binop_; + BinOpeInfo info_; + const Definition &rule_; + +private: + size_t parse_expression(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt, size_t min_prec) const; - std::string name_; + Definition &get_reference_for_binop(Context &c) const; }; /* * Factories */ -template <typename... Args> -std::shared_ptr<Ope> seq(Args&& ...args) { - return std::make_shared<Sequence>(static_cast<std::shared_ptr<Ope>>(args)...); +template <typename... Args> std::shared_ptr<Ope> seq(Args &&... args) { + return std::make_shared<Sequence>(static_cast<std::shared_ptr<Ope>>(args)...); +} + +template <typename... Args> std::shared_ptr<Ope> cho(Args &&... args) { + return std::make_shared<PrioritizedChoice>( + static_cast<std::shared_ptr<Ope>>(args)...); +} + +inline std::shared_ptr<Ope> zom(const std::shared_ptr<Ope> &ope) { + return Repetition::zom(ope); +} + +inline std::shared_ptr<Ope> oom(const std::shared_ptr<Ope> &ope) { + return Repetition::oom(ope); +} + +inline std::shared_ptr<Ope> opt(const std::shared_ptr<Ope> &ope) { + return Repetition::opt(ope); +} + +inline std::shared_ptr<Ope> rep(const std::shared_ptr<Ope> &ope, size_t min, + size_t max) { + return std::make_shared<Repetition>(ope, min, max); } -template <typename... Args> -std::shared_ptr<Ope> cho(Args&& ...args) { - return std::make_shared<PrioritizedChoice>(static_cast<std::shared_ptr<Ope>>(args)...); +inline std::shared_ptr<Ope> apd(const std::shared_ptr<Ope> &ope) { + return std::make_shared<AndPredicate>(ope); } -inline std::shared_ptr<Ope> zom(const std::shared_ptr<Ope>& ope) { - return std::make_shared<ZeroOrMore>(ope); +inline std::shared_ptr<Ope> npd(const std::shared_ptr<Ope> &ope) { + return std::make_shared<NotPredicate>(ope); } -inline std::shared_ptr<Ope> oom(const std::shared_ptr<Ope>& ope) { - return std::make_shared<OneOrMore>(ope); +inline std::shared_ptr<Ope> dic(const std::vector<std::string> &v) { + return std::make_shared<Dictionary>(v); } -inline std::shared_ptr<Ope> opt(const std::shared_ptr<Ope>& ope) { - return std::make_shared<Option>(ope); +inline std::shared_ptr<Ope> lit(const std::string &s) { + return std::make_shared<LiteralString>(s, false); } -inline std::shared_ptr<Ope> apd(const std::shared_ptr<Ope>& ope) { - return std::make_shared<AndPredicate>(ope); +inline std::shared_ptr<Ope> liti(const std::string &s) { + return std::make_shared<LiteralString>(s, true); } -inline std::shared_ptr<Ope> npd(const std::shared_ptr<Ope>& ope) { - return std::make_shared<NotPredicate>(ope); +inline std::shared_ptr<Ope> cls(const std::string &s) { + return std::make_shared<CharacterClass>(s, false); } -inline std::shared_ptr<Ope> lit(const std::string& lit) { - return std::make_shared<LiteralString>(lit); +inline std::shared_ptr<Ope> +cls(const std::vector<std::pair<char32_t, char32_t>> &ranges) { + return std::make_shared<CharacterClass>(ranges, false); } -inline std::shared_ptr<Ope> cls(const std::string& s) { - return std::make_shared<CharacterClass>(s); +inline std::shared_ptr<Ope> ncls(const std::string &s) { + return std::make_shared<CharacterClass>(s, true); } -inline std::shared_ptr<Ope> cls(const std::vector<std::pair<char32_t, char32_t>>& ranges) { - return std::make_shared<CharacterClass>(ranges); +inline std::shared_ptr<Ope> +ncls(const std::vector<std::pair<char32_t, char32_t>> &ranges) { + return std::make_shared<CharacterClass>(ranges, true); } inline std::shared_ptr<Ope> chr(char dt) { - return std::make_shared<Character>(dt); + return std::make_shared<Character>(dt); } -inline std::shared_ptr<Ope> dot() { - return std::make_shared<AnyCharacter>(); +inline std::shared_ptr<Ope> dot() { return std::make_shared<AnyCharacter>(); } + +inline std::shared_ptr<Ope> csc(const std::shared_ptr<Ope> &ope) { + return std::make_shared<CaptureScope>(ope); } -inline std::shared_ptr<Ope> csc(const std::shared_ptr<Ope>& ope) { - return std::make_shared<CaptureScope>(ope); +inline std::shared_ptr<Ope> cap(const std::shared_ptr<Ope> &ope, + Capture::MatchAction ma) { + return std::make_shared<Capture>(ope, ma); } -inline std::shared_ptr<Ope> cap(const std::shared_ptr<Ope>& ope, Capture::MatchAction ma) { - return std::make_shared<Capture>(ope, ma); +inline std::shared_ptr<Ope> tok(const std::shared_ptr<Ope> &ope) { + return std::make_shared<TokenBoundary>(ope); } -inline std::shared_ptr<Ope> tok(const std::shared_ptr<Ope>& ope) { - return std::make_shared<TokenBoundary>(ope); +inline std::shared_ptr<Ope> ign(const std::shared_ptr<Ope> &ope) { + return std::make_shared<Ignore>(ope); } -inline std::shared_ptr<Ope> ign(const std::shared_ptr<Ope>& ope) { - return std::make_shared<Ignore>(ope); +inline std::shared_ptr<Ope> +usr(std::function<size_t(const char *s, size_t n, SemanticValues &sv, any &dt)> + fn) { + return std::make_shared<User>(fn); } -inline std::shared_ptr<Ope> usr(std::function<size_t (const char* s, size_t n, SemanticValues& sv, any& dt)> fn) { - return std::make_shared<User>(fn); +inline std::shared_ptr<Ope> ref(const Grammar &grammar, const std::string &name, + const char *s, bool is_macro, + const std::vector<std::shared_ptr<Ope>> &args) { + return std::make_shared<Reference>(grammar, name, s, is_macro, args); } -inline std::shared_ptr<Ope> ref(const Grammar& grammar, const std::string& name, const char* s, bool is_macro, const std::vector<std::shared_ptr<Ope>>& args) { - return std::make_shared<Reference>(grammar, name, s, is_macro, args); +inline std::shared_ptr<Ope> wsp(const std::shared_ptr<Ope> &ope) { + return std::make_shared<Whitespace>(std::make_shared<Ignore>(ope)); } -inline std::shared_ptr<Ope> wsp(const std::shared_ptr<Ope>& ope) { - return std::make_shared<Whitespace>(std::make_shared<Ignore>(ope)); +inline std::shared_ptr<Ope> bkr(const std::string &name) { + return std::make_shared<BackReference>(name); } -inline std::shared_ptr<Ope> bkr(const std::string& name) { - return std::make_shared<BackReference>(name); +inline std::shared_ptr<Ope> pre(const std::shared_ptr<Ope> &atom, + const std::shared_ptr<Ope> &binop, + const PrecedenceClimbing::BinOpeInfo &info, + const Definition &rule) { + return std::make_shared<PrecedenceClimbing>(atom, binop, info, rule); } /* * Visitor */ -struct Ope::Visitor -{ - virtual ~Visitor() {} - virtual void visit(Sequence& /*ope*/) {} - virtual void visit(PrioritizedChoice& /*ope*/) {} - virtual void visit(ZeroOrMore& /*ope*/) {} - virtual void visit(OneOrMore& /*ope*/) {} - virtual void visit(Option& /*ope*/) {} - virtual void visit(AndPredicate& /*ope*/) {} - virtual void visit(NotPredicate& /*ope*/) {} - virtual void visit(LiteralString& /*ope*/) {} - virtual void visit(CharacterClass& /*ope*/) {} - virtual void visit(Character& /*ope*/) {} - virtual void visit(AnyCharacter& /*ope*/) {} - virtual void visit(CaptureScope& /*ope*/) {} - virtual void visit(Capture& /*ope*/) {} - virtual void visit(TokenBoundary& /*ope*/) {} - virtual void visit(Ignore& /*ope*/) {} - virtual void visit(User& /*ope*/) {} - virtual void visit(WeakHolder& /*ope*/) {} - virtual void visit(Holder& /*ope*/) {} - virtual void visit(Reference& /*ope*/) {} - virtual void visit(Whitespace& /*ope*/) {} - virtual void visit(BackReference& /*ope*/) {} +struct Ope::Visitor { + virtual ~Visitor() {} + virtual void visit(Sequence & /*ope*/) {} + virtual void visit(PrioritizedChoice & /*ope*/) {} + virtual void visit(Repetition & /*ope*/) {} + virtual void visit(AndPredicate & /*ope*/) {} + virtual void visit(NotPredicate & /*ope*/) {} + virtual void visit(Dictionary & /*ope*/) {} + virtual void visit(LiteralString & /*ope*/) {} + virtual void visit(CharacterClass & /*ope*/) {} + virtual void visit(Character & /*ope*/) {} + virtual void visit(AnyCharacter & /*ope*/) {} + virtual void visit(CaptureScope & /*ope*/) {} + virtual void visit(Capture & /*ope*/) {} + virtual void visit(TokenBoundary & /*ope*/) {} + virtual void visit(Ignore & /*ope*/) {} + virtual void visit(User & /*ope*/) {} + virtual void visit(WeakHolder & /*ope*/) {} + virtual void visit(Holder & /*ope*/) {} + virtual void visit(Reference & /*ope*/) {} + virtual void visit(Whitespace & /*ope*/) {} + virtual void visit(BackReference & /*ope*/) {} + virtual void visit(PrecedenceClimbing & /*ope*/) {} }; -struct AssignIDToDefinition : public Ope::Visitor -{ - using Ope::Visitor::visit; +struct IsReference : public Ope::Visitor { + void visit(Reference & /*ope*/) override { is_reference = true; } + bool is_reference = false; +}; - void visit(Sequence& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } +struct TraceOpeName : public Ope::Visitor { + void visit(Sequence & /*ope*/) override { name = "Sequence"; } + void visit(PrioritizedChoice & /*ope*/) override { + name = "PrioritizedChoice"; + } + void visit(Repetition & /*ope*/) override { name = "Repetition"; } + void visit(AndPredicate & /*ope*/) override { name = "AndPredicate"; } + void visit(NotPredicate & /*ope*/) override { name = "NotPredicate"; } + void visit(Dictionary & /*ope*/) override { name = "Dictionary"; } + void visit(LiteralString & /*ope*/) override { name = "LiteralString"; } + void visit(CharacterClass & /*ope*/) override { name = "CharacterClass"; } + void visit(Character & /*ope*/) override { name = "Character"; } + void visit(AnyCharacter & /*ope*/) override { name = "AnyCharacter"; } + void visit(CaptureScope & /*ope*/) override { name = "CaptureScope"; } + void visit(Capture & /*ope*/) override { name = "Capture"; } + void visit(TokenBoundary & /*ope*/) override { name = "TokenBoundary"; } + void visit(Ignore & /*ope*/) override { name = "Ignore"; } + void visit(User & /*ope*/) override { name = "User"; } + void visit(WeakHolder & /*ope*/) override { name = "WeakHolder"; } + void visit(Holder &ope) override { name = ope.trace_name(); } + void visit(Reference & /*ope*/) override { name = "Reference"; } + void visit(Whitespace & /*ope*/) override { name = "Whitespace"; } + void visit(BackReference & /*ope*/) override { name = "BackReference"; } + void visit(PrecedenceClimbing & /*ope*/) override { + name = "PrecedenceClimbing"; + } + + const char *name = nullptr; +}; + +struct AssignIDToDefinition : public Ope::Visitor { + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(PrioritizedChoice& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } - void visit(Option& ope) override { ope.ope_->accept(*this); } - void visit(AndPredicate& ope) override { ope.ope_->accept(*this); } - void visit(NotPredicate& ope) override { ope.ope_->accept(*this); } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); } - void visit(Capture& ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); } - void visit(Ignore& ope) override { ope.ope_->accept(*this); } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder& ope) override; - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); } - - std::unordered_map<void*, size_t> ids; + } + void visit(Repetition &ope) override { ope.ope_->accept(*this); } + void visit(AndPredicate &ope) override { ope.ope_->accept(*this); } + void visit(NotPredicate &ope) override { ope.ope_->accept(*this); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override; + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + + std::unordered_map<void *, size_t> ids; }; -struct TokenChecker : public Ope::Visitor -{ - TokenChecker() : has_token_boundary_(false), has_rule_(false) {} +struct IsLiteralToken : public Ope::Visitor { + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + if (!IsLiteralToken::check(*op)) { return; } + } + result_ = true; + } - using Ope::Visitor::visit; + void visit(Dictionary & /*ope*/) override { result_ = true; } + void visit(LiteralString & /*ope*/) override { result_ = true; } - void visit(Sequence& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + static bool check(Ope &ope) { + IsLiteralToken vis; + ope.accept(vis); + return vis.result_; + } + +private: + bool result_ = false; +}; + +struct TokenChecker : public Ope::Visitor { + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(PrioritizedChoice& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } - void visit(Option& ope) override { ope.ope_->accept(*this); } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); } - void visit(Capture& ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary& /*ope*/) override { has_token_boundary_ = true; } - void visit(Ignore& ope) override { ope.ope_->accept(*this); } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); } + } + void visit(Repetition &ope) override { ope.ope_->accept(*this); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary & /*ope*/) override { has_token_boundary_ = true; } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } + + static bool is_token(Ope &ope) { + if (IsLiteralToken::check(ope)) { return true; } + + TokenChecker vis; + ope.accept(vis); + return vis.has_token_boundary_ || !vis.has_rule_; + } + +private: + bool has_token_boundary_ = false; + bool has_rule_ = false; +}; - bool is_token() const { - return has_token_boundary_ || !has_rule_; +struct DetectLeftRecursion : public Ope::Visitor { + DetectLeftRecursion(const std::string &name) : name_(name) {} + + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); + if (done_) { + break; + } else if (error_s) { + done_ = true; + break; + } + } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); + if (error_s) { + done_ = true; + break; + } } + } + void visit(Repetition &ope) override { + ope.ope_->accept(*this); + done_ = ope.min_ > 0; + } + void visit(AndPredicate &ope) override { + ope.ope_->accept(*this); + done_ = false; + } + void visit(NotPredicate &ope) override { + ope.ope_->accept(*this); + done_ = false; + } + void visit(Dictionary & /*ope*/) override { done_ = true; } + void visit(LiteralString &ope) override { done_ = !ope.lit_.empty(); } + void visit(CharacterClass & /*ope*/) override { done_ = true; } + void visit(Character & /*ope*/) override { done_ = true; } + void visit(AnyCharacter & /*ope*/) override { done_ = true; } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(User & /*ope*/) override { done_ = true; } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(BackReference & /*ope*/) override { done_ = true; } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } + + const char *error_s = nullptr; private: - bool has_token_boundary_; - bool has_rule_; + std::string name_; + std::set<std::string> refs_; + bool done_ = false; }; -struct DetectLeftRecursion : public Ope::Visitor { - DetectLeftRecursion(const std::string& name) - : error_s(nullptr), name_(name), done_(false) {} +struct HasEmptyElement : public Ope::Visitor { + HasEmptyElement(std::list<std::pair<const char *, std::string>> &refs) + : refs_(refs) {} + + void visit(Sequence &ope) override { + bool save_is_empty = false; + const char *save_error_s = nullptr; + std::string save_error_name; + for (auto op : ope.opes_) { + op->accept(*this); + if (!is_empty) { return; } + save_is_empty = is_empty; + save_error_s = error_s; + save_error_name = error_name; + is_empty = false; + error_name.clear(); + } + is_empty = save_is_empty; + error_s = save_error_s; + error_name = save_error_name; + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); + if (is_empty) { return; } + } + } + void visit(Repetition &ope) override { + if (ope.min_ == 0) { + set_error(); + } else { + ope.ope_->accept(*this); + } + } + void visit(AndPredicate & /*ope*/) override { set_error(); } + void visit(NotPredicate & /*ope*/) override { set_error(); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } + + bool is_empty = false; + const char *error_s = nullptr; + std::string error_name; - using Ope::Visitor::visit; +private: + void set_error() { + is_empty = true; + error_s = refs_.back().first; + error_name = refs_.back().second; + } + std::list<std::pair<const char *, std::string>> &refs_; +}; - void visit(Sequence& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - if (done_) { - break; - } else if (error_s) { - done_ = true; - break; - } - } +struct DetectInfiniteLoop : public Ope::Visitor { + DetectInfiniteLoop(const char *s, const std::string &name) { + refs_.emplace_back(s, name); + } + + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); + if (has_error) { return; } } - void visit(PrioritizedChoice& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - if (error_s) { - done_ = true; - break; - } - } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); + if (has_error) { return; } + } + } + void visit(Repetition &ope) override { + if (ope.max_ == std::numeric_limits<size_t>::max()) { + HasEmptyElement vis(refs_); + ope.ope_->accept(vis); + if (vis.is_empty) { + has_error = true; + error_s = vis.error_s; + error_name = vis.error_name; + } + } else { + ope.ope_->accept(*this); } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); done_ = false; } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); done_ = true; } - void visit(Option& ope) override { ope.ope_->accept(*this); done_ = false; } - void visit(AndPredicate& ope) override { ope.ope_->accept(*this); done_ = false; } - void visit(NotPredicate& ope) override { ope.ope_->accept(*this); done_ = false; } - void visit(LiteralString& ope) override { done_ = !ope.lit_.empty(); } - void visit(CharacterClass& /*ope*/) override { done_ = true; } - void visit(Character& /*ope*/) override { done_ = true; } - void visit(AnyCharacter& /*ope*/) override { done_ = true; } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); } - void visit(Capture& ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); } - void visit(Ignore& ope) override { ope.ope_->accept(*this); } - void visit(User& /*ope*/) override { done_ = true; } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder& ope) override { ope.ope_->accept(*this); } - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); } - void visit(BackReference& /*ope*/) override { done_ = true; } - - const char* error_s; + } + void visit(AndPredicate &ope) override { ope.ope_->accept(*this); } + void visit(NotPredicate &ope) override { ope.ope_->accept(*this); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } + + bool has_error = false; + const char *error_s = nullptr; + std::string error_name; private: - std::string name_; - std::set<std::string> refs_; - bool done_; + std::list<std::pair<const char *, std::string>> refs_; }; struct ReferenceChecker : public Ope::Visitor { - ReferenceChecker( - const Grammar& grammar, - const std::vector<std::string>& params) - : grammar_(grammar), params_(params) {} + ReferenceChecker(const Grammar &grammar, + const std::vector<std::string> ¶ms) + : grammar_(grammar), params_(params) {} - using Ope::Visitor::visit; - - void visit(Sequence& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(PrioritizedChoice& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } - void visit(Option& ope) override { ope.ope_->accept(*this); } - void visit(AndPredicate& ope) override { ope.ope_->accept(*this); } - void visit(NotPredicate& ope) override { ope.ope_->accept(*this); } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); } - void visit(Capture& ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); } - void visit(Ignore& ope) override { ope.ope_->accept(*this); } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder& ope) override { ope.ope_->accept(*this); } - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); } - - std::unordered_map<std::string, const char*> error_s; - std::unordered_map<std::string, std::string> error_message; + } + void visit(Repetition &ope) override { ope.ope_->accept(*this); } + void visit(AndPredicate &ope) override { ope.ope_->accept(*this); } + void visit(NotPredicate &ope) override { ope.ope_->accept(*this); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } + + std::unordered_map<std::string, const char *> error_s; + std::unordered_map<std::string, std::string> error_message; private: - const Grammar& grammar_; - const std::vector<std::string>& params_; + const Grammar &grammar_; + const std::vector<std::string> ¶ms_; }; struct LinkReferences : public Ope::Visitor { - LinkReferences( - Grammar& grammar, - const std::vector<std::string>& params) - : grammar_(grammar), params_(params) {} + LinkReferences(Grammar &grammar, const std::vector<std::string> ¶ms) + : grammar_(grammar), params_(params) {} - using Ope::Visitor::visit; - - void visit(Sequence& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + void visit(Sequence &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(PrioritizedChoice& ope) override { - for (auto op: ope.opes_) { - op->accept(*this); - } + } + void visit(PrioritizedChoice &ope) override { + for (auto op : ope.opes_) { + op->accept(*this); } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); } - void visit(Option& ope) override { ope.ope_->accept(*this); } - void visit(AndPredicate& ope) override { ope.ope_->accept(*this); } - void visit(NotPredicate& ope) override { ope.ope_->accept(*this); } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); } - void visit(Capture& ope) override { ope.ope_->accept(*this); } - void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); } - void visit(Ignore& ope) override { ope.ope_->accept(*this); } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder& ope) override { ope.ope_->accept(*this); } - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); } + } + void visit(Repetition &ope) override { ope.ope_->accept(*this); } + void visit(AndPredicate &ope) override { ope.ope_->accept(*this); } + void visit(NotPredicate &ope) override { ope.ope_->accept(*this); } + void visit(CaptureScope &ope) override { ope.ope_->accept(*this); } + void visit(Capture &ope) override { ope.ope_->accept(*this); } + void visit(TokenBoundary &ope) override { ope.ope_->accept(*this); } + void visit(Ignore &ope) override { ope.ope_->accept(*this); } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { ope.ope_->accept(*this); } + void visit(PrecedenceClimbing &ope) override { ope.atom_->accept(*this); } private: - Grammar& grammar_; - const std::vector<std::string>& params_; + Grammar &grammar_; + const std::vector<std::string> ¶ms_; }; struct FindReference : public Ope::Visitor { - FindReference( - const std::vector<std::shared_ptr<Ope>>& args, - const std::vector<std::string>& params) - : args_(args), params_(params) {} - - using Ope::Visitor::visit; + FindReference(const std::vector<std::shared_ptr<Ope>> &args, + const std::vector<std::string> ¶ms) + : args_(args), params_(params) {} - void visit(Sequence& ope) override { - std::vector<std::shared_ptr<Ope>> opes; - for (auto o: ope.opes_) { - o->accept(*this); - opes.push_back(found_ope); - } - found_ope = std::make_shared<Sequence>(opes); + void visit(Sequence &ope) override { + std::vector<std::shared_ptr<Ope>> opes; + for (auto o : ope.opes_) { + o->accept(*this); + opes.push_back(found_ope); } - void visit(PrioritizedChoice& ope) override { - std::vector<std::shared_ptr<Ope>> opes; - for (auto o: ope.opes_) { - o->accept(*this); - opes.push_back(found_ope); - } - found_ope = std::make_shared<PrioritizedChoice>(opes); - } - void visit(ZeroOrMore& ope) override { ope.ope_->accept(*this); found_ope = zom(found_ope); } - void visit(OneOrMore& ope) override { ope.ope_->accept(*this); found_ope = oom(found_ope); } - void visit(Option& ope) override { ope.ope_->accept(*this); found_ope = opt(found_ope); } - void visit(AndPredicate& ope) override { ope.ope_->accept(*this); found_ope = apd(found_ope); } - void visit(NotPredicate& ope) override { ope.ope_->accept(*this); found_ope = npd(found_ope); } - void visit(LiteralString& ope) override { found_ope = ope.shared_from_this(); } - void visit(CharacterClass& ope) override { found_ope = ope.shared_from_this(); } - void visit(Character& ope) override { found_ope = ope.shared_from_this(); } - void visit(AnyCharacter& ope) override { found_ope = ope.shared_from_this(); } - void visit(CaptureScope& ope) override { ope.ope_->accept(*this); found_ope = csc(found_ope); } - void visit(Capture& ope) override { ope.ope_->accept(*this); found_ope = cap(found_ope, ope.match_action_); } - void visit(TokenBoundary& ope) override { ope.ope_->accept(*this); found_ope = tok(found_ope); } - void visit(Ignore& ope) override { ope.ope_->accept(*this); found_ope = ign(found_ope); } - void visit(WeakHolder& ope) override { ope.weak_.lock()->accept(*this); } - void visit(Holder& ope) override { ope.ope_->accept(*this); } - void visit(Reference& ope) override; - void visit(Whitespace& ope) override { ope.ope_->accept(*this); found_ope = wsp(found_ope); } - - std::shared_ptr<Ope> found_ope; + found_ope = std::make_shared<Sequence>(opes); + } + void visit(PrioritizedChoice &ope) override { + std::vector<std::shared_ptr<Ope>> opes; + for (auto o : ope.opes_) { + o->accept(*this); + opes.push_back(found_ope); + } + found_ope = std::make_shared<PrioritizedChoice>(opes); + } + void visit(Repetition &ope) override { + ope.ope_->accept(*this); + found_ope = rep(found_ope, ope.min_, ope.max_); + } + void visit(AndPredicate &ope) override { + ope.ope_->accept(*this); + found_ope = apd(found_ope); + } + void visit(NotPredicate &ope) override { + ope.ope_->accept(*this); + found_ope = npd(found_ope); + } + void visit(Dictionary &ope) override { found_ope = ope.shared_from_this(); } + void visit(LiteralString &ope) override { + found_ope = ope.shared_from_this(); + } + void visit(CharacterClass &ope) override { + found_ope = ope.shared_from_this(); + } + void visit(Character &ope) override { found_ope = ope.shared_from_this(); } + void visit(AnyCharacter &ope) override { found_ope = ope.shared_from_this(); } + void visit(CaptureScope &ope) override { + ope.ope_->accept(*this); + found_ope = csc(found_ope); + } + void visit(Capture &ope) override { + ope.ope_->accept(*this); + found_ope = cap(found_ope, ope.match_action_); + } + void visit(TokenBoundary &ope) override { + ope.ope_->accept(*this); + found_ope = tok(found_ope); + } + void visit(Ignore &ope) override { + ope.ope_->accept(*this); + found_ope = ign(found_ope); + } + void visit(WeakHolder &ope) override { ope.weak_.lock()->accept(*this); } + void visit(Holder &ope) override { ope.ope_->accept(*this); } + void visit(Reference &ope) override; + void visit(Whitespace &ope) override { + ope.ope_->accept(*this); + found_ope = wsp(found_ope); + } + void visit(PrecedenceClimbing &ope) override { + ope.atom_->accept(*this); + found_ope = csc(found_ope); + } + + std::shared_ptr<Ope> found_ope; + +private: + const std::vector<std::shared_ptr<Ope>> &args_; + const std::vector<std::string> ¶ms_; +}; + +struct IsPrioritizedChoice : public Ope::Visitor { + void visit(PrioritizedChoice & /*ope*/) override { result_ = true; } + + static bool check(Ope &ope) { + IsPrioritizedChoice vis; + ope.accept(vis); + return vis.result_; + } private: - const std::vector<std::shared_ptr<Ope>>& args_; - const std::vector<std::string>& params_; + bool result_ = false; }; /* * Keywords */ -static const char* WHITESPACE_DEFINITION_NAME = "%whitespace"; -static const char* WORD_DEFINITION_NAME = "%word"; +static const char *WHITESPACE_DEFINITION_NAME = "%whitespace"; +static const char *WORD_DEFINITION_NAME = "%word"; /* * Definition */ -class Definition -{ +class Definition { public: - struct Result { - bool ret; - size_t len; - const char* error_pos; - const char* message_pos; - const std::string message; - }; + struct Result { + bool ret; + size_t len; + const char *error_pos; + const char *message_pos; + const std::string message; + }; - Definition() - : ignoreSemanticValue(false) - , enablePackratParsing(false) - , is_macro(false) - , holder_(std::make_shared<Holder>(this)) - , is_token_(false) {} - - Definition(const Definition& rhs) - : name(rhs.name) - , ignoreSemanticValue(false) - , enablePackratParsing(false) - , is_macro(false) - , holder_(rhs.holder_) - , is_token_(false) - { - holder_->outer_ = this; - } - - Definition(Definition&& rhs) - : name(std::move(rhs.name)) - , ignoreSemanticValue(rhs.ignoreSemanticValue) - , whitespaceOpe(rhs.whitespaceOpe) - , wordOpe(rhs.wordOpe) - , enablePackratParsing(rhs.enablePackratParsing) - , is_macro(rhs.is_macro) - , holder_(std::move(rhs.holder_)) - , is_token_(rhs.is_token_) - { - holder_->outer_ = this; - } + Definition() : holder_(std::make_shared<Holder>(this)) {} - Definition(const std::shared_ptr<Ope>& ope) - : ignoreSemanticValue(false) - , enablePackratParsing(false) - , is_macro(false) - , holder_(std::make_shared<Holder>(this)) - , is_token_(false) - { - *this <= ope; - } + Definition(const Definition &rhs) : name(rhs.name), holder_(rhs.holder_) { + holder_->outer_ = this; + } - operator std::shared_ptr<Ope>() { - return std::make_shared<WeakHolder>(holder_); - } + Definition(const std::shared_ptr<Ope> &ope) + : holder_(std::make_shared<Holder>(this)) { + *this <= ope; + } - Definition& operator<=(const std::shared_ptr<Ope>& ope) { - holder_->ope_ = ope; - return *this; - } + operator std::shared_ptr<Ope>() { + return std::make_shared<WeakHolder>(holder_); + } - Result parse(const char* s, size_t n, const char* path = nullptr) const { - SemanticValues sv; - any dt; - return parse_core(s, n, sv, dt, path); - } + Definition &operator<=(const std::shared_ptr<Ope> &ope) { + holder_->ope_ = ope; + return *this; + } - Result parse(const char* s, const char* path = nullptr) const { - auto n = strlen(s); - return parse(s, n, path); - } + Result parse(const char *s, size_t n, const char *path = nullptr) const { + SemanticValues sv; + any dt; + return parse_core(s, n, sv, dt, path); + } - Result parse(const char* s, size_t n, any& dt, const char* path = nullptr) const { - SemanticValues sv; - return parse_core(s, n, sv, dt, path); - } + Result parse(const char *s, const char *path = nullptr) const { + auto n = strlen(s); + return parse(s, n, path); + } - Result parse(const char* s, any& dt, const char* path = nullptr) const { - auto n = strlen(s); - return parse(s, n, dt, path); - } + Result parse(const char *s, size_t n, any &dt, + const char *path = nullptr) const { + SemanticValues sv; + return parse_core(s, n, sv, dt, path); + } - template <typename T> - Result parse_and_get_value(const char* s, size_t n, T& val, const char* path = nullptr) const { - SemanticValues sv; - any dt; - auto r = parse_core(s, n, sv, dt, path); - if (r.ret && !sv.empty() && !sv.front().is_undefined()) { - val = sv[0].get<T>(); - } - return r; - } + Result parse(const char *s, any &dt, const char *path = nullptr) const { + auto n = strlen(s); + return parse(s, n, dt, path); + } - template <typename T> - Result parse_and_get_value(const char* s, T& val, const char* path = nullptr) const { - auto n = strlen(s); - return parse_and_get_value(s, n, val, path); + template <typename T> + Result parse_and_get_value(const char *s, size_t n, T &val, + const char *path = nullptr) const { + SemanticValues sv; + any dt; + auto r = parse_core(s, n, sv, dt, path); + if (r.ret && !sv.empty() && sv.front().has_value()) { + val = any_cast<T>(sv[0]); } + return r; + } - template <typename T> - Result parse_and_get_value(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { - SemanticValues sv; - auto r = parse_core(s, n, sv, dt, path); - if (r.ret && !sv.empty() && !sv.front().is_undefined()) { - val = sv[0].get<T>(); - } - return r; - } + template <typename T> + Result parse_and_get_value(const char *s, T &val, + const char *path = nullptr) const { + auto n = strlen(s); + return parse_and_get_value(s, n, val, path); + } - template <typename T> - Result parse_and_get_value(const char* s, any& dt, T& val, const char* path = nullptr) const { - auto n = strlen(s); - return parse_and_get_value(s, n, dt, val, path); + template <typename T> + Result parse_and_get_value(const char *s, size_t n, any &dt, T &val, + const char *path = nullptr) const { + SemanticValues sv; + auto r = parse_core(s, n, sv, dt, path); + if (r.ret && !sv.empty() && sv.front().has_value()) { + val = any_cast<T>(sv[0]); } + return r; + } - Action operator=(Action a) { - action = a; - return a; - } + template <typename T> + Result parse_and_get_value(const char *s, any &dt, T &val, + const char *path = nullptr) const { + auto n = strlen(s); + return parse_and_get_value(s, n, dt, val, path); + } - template <typename T> - Definition& operator,(T fn) { - operator=(fn); - return *this; - } + Action operator=(Action a) { + action = a; + return a; + } - Definition& operator~() { - ignoreSemanticValue = true; - return *this; - } + template <typename T> Definition &operator,(T fn) { + operator=(fn); + return *this; + } - void accept(Ope::Visitor& v) { - holder_->accept(v); - } + Definition &operator~() { + ignoreSemanticValue = true; + return *this; + } - std::shared_ptr<Ope> get_core_operator() const { - return holder_->ope_; - } + void accept(Ope::Visitor &v) { holder_->accept(v); } - bool is_token() const { - std::call_once(is_token_init_, [this]() { - TokenChecker vis; - get_core_operator()->accept(vis); - is_token_ = vis.is_token(); - }); - return is_token_; - } - - std::string name; - size_t id; - Action action; - std::function<void (const char* s, size_t n, any& dt)> enter; - std::function<void (const char* s, size_t n, size_t matchlen, any& value, any& dt)> leave; - std::function<std::string ()> error_message; - bool ignoreSemanticValue; - std::shared_ptr<Ope> whitespaceOpe; - std::shared_ptr<Ope> wordOpe; - bool enablePackratParsing; - bool is_macro; - std::vector<std::string> params; - Tracer tracer; + std::shared_ptr<Ope> get_core_operator() const { return holder_->ope_; } -private: - friend class Reference; + bool is_token() const { + std::call_once(is_token_init_, [this]() { + is_token_ = TokenChecker::is_token(*get_core_operator()); + }); + return is_token_; + } - Definition& operator=(const Definition& rhs); - Definition& operator=(Definition&& rhs); + std::string name; + const char *s_ = nullptr; + + size_t id = 0; + Action action; + std::function<void(const char *s, size_t n, any &dt)> enter; + std::function<void(const char *s, size_t n, size_t matchlen, any &value, + any &dt)> + leave; + std::function<std::string()> error_message; + bool ignoreSemanticValue = false; + std::shared_ptr<Ope> whitespaceOpe; + std::shared_ptr<Ope> wordOpe; + bool enablePackratParsing = false; + bool is_macro = false; + std::vector<std::string> params; + TracerEnter tracer_enter; + TracerLeave tracer_leave; + bool disable_action = false; - Result parse_core(const char* s, size_t n, SemanticValues& sv, any& dt, const char* path) const { - std::shared_ptr<Ope> ope = holder_; +private: + friend class Reference; + friend class ParserGenerator; + + Definition &operator=(const Definition &rhs); + Definition &operator=(Definition &&rhs); + + void initialize_definition_ids() const { + std::call_once(definition_ids_init_, [&]() { + AssignIDToDefinition vis; + holder_->accept(vis); + if (whitespaceOpe) { whitespaceOpe->accept(vis); } + if (wordOpe) { wordOpe->accept(vis); } + definition_ids_.swap(vis.ids); + }); + } - AssignIDToDefinition vis; - holder_->accept(vis); + Result parse_core(const char *s, size_t n, SemanticValues &sv, any &dt, + const char *path) const { + initialize_definition_ids(); - if (whitespaceOpe) { - ope = std::make_shared<Sequence>(whitespaceOpe, ope); - whitespaceOpe->accept(vis); - } + std::shared_ptr<Ope> ope = holder_; + if (whitespaceOpe) { ope = std::make_shared<Sequence>(whitespaceOpe, ope); } - if (wordOpe) { - wordOpe->accept(vis); - } + Context cxt(path, s, n, definition_ids_.size(), whitespaceOpe, wordOpe, + enablePackratParsing, tracer_enter, tracer_leave); - Context cxt(path, s, n, vis.ids.size(), whitespaceOpe, wordOpe, enablePackratParsing, tracer); - auto len = ope->parse(s, n, sv, cxt, dt); - return Result{ success(len), len, cxt.error_pos, cxt.message_pos, cxt.message }; - } + auto len = ope->parse(s, n, sv, cxt, dt); + return Result{success(len), len, cxt.error_pos, cxt.message_pos, + cxt.message}; + } - std::shared_ptr<Holder> holder_; - mutable std::once_flag is_token_init_; - mutable bool is_token_; + std::shared_ptr<Holder> holder_; + mutable std::once_flag is_token_init_; + mutable bool is_token_ = false; + mutable std::once_flag assign_id_to_definition_init_; + mutable std::once_flag definition_ids_init_; + mutable std::unordered_map<void *, size_t> definition_ids_; }; /* * Implementations */ -inline size_t parse_literal(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt, - const std::string& lit, bool& init_is_word, bool& is_word) -{ - size_t i = 0; - for (; i < lit.size(); i++) { - if (i >= n || s[i] != lit[i]) { - c.set_error_pos(s); - return static_cast<size_t>(-1); - } +inline size_t parse_literal(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt, const std::string &lit, + bool &init_is_word, bool &is_word, + bool ignore_case) { + size_t i = 0; + for (; i < lit.size(); i++) { + if (i >= n || (ignore_case ? (std::tolower(s[i]) != std::tolower(lit[i])) + : (s[i] != lit[i]))) { + c.set_error_pos(s); + return static_cast<size_t>(-1); } + } - // Word check - static Context dummy_c(nullptr, lit.data(), lit.size(), 0, nullptr, nullptr, false, nullptr); - static SemanticValues dummy_sv; - static any dummy_dt; + // Word check + static Context dummy_c(nullptr, c.s, c.l, 0, nullptr, nullptr, false, nullptr, + nullptr); + static SemanticValues dummy_sv; + static any dummy_dt; - if (!init_is_word) { // TODO: Protect with mutex - if (c.wordOpe) { - auto len = c.wordOpe->parse(lit.data(), lit.size(), dummy_sv, dummy_c, dummy_dt); - is_word = success(len); - } - init_is_word = true; + if (!init_is_word) { // TODO: Protect with mutex + if (c.wordOpe) { + auto len = + c.wordOpe->parse(lit.data(), lit.size(), dummy_sv, dummy_c, dummy_dt); + is_word = success(len); } + init_is_word = true; + } - if (is_word) { - auto ope = std::make_shared<NotPredicate>(c.wordOpe); - auto len = ope->parse(s + i, n - i, dummy_sv, dummy_c, dummy_dt); - if (fail(len)) { - return static_cast<size_t>(-1); - } - i += len; - } + if (is_word) { + auto ope = std::make_shared<NotPredicate>(c.wordOpe); + auto len = ope->parse(s + i, n - i, dummy_sv, dummy_c, dummy_dt); + if (fail(len)) { return static_cast<size_t>(-1); } + i += len; + } - // Skip whiltespace - if (!c.in_token) { - if (c.whitespaceOpe) { - auto len = c.whitespaceOpe->parse(s + i, n - i, sv, c, dt); - if (fail(len)) { - return static_cast<size_t>(-1); - } - i += len; - } + // Skip whiltespace + if (!c.in_token) { + if (c.whitespaceOpe) { + auto len = c.whitespaceOpe->parse(s + i, n - i, sv, c, dt); + if (fail(len)) { return static_cast<size_t>(-1); } + i += len; } + } - return i; + return i; } -inline size_t LiteralString::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { - c.trace("LiteralString", s, n, sv, dt); - return parse_literal(s, n, sv, c, dt, lit_, init_is_word_, is_word_); +inline void Context::trace_enter(const char *name, const char *a_s, size_t n, + SemanticValues &sv, any &dt) const { + trace_ids.push_back(next_trace_id++); + tracer_enter(name, a_s, n, sv, *this, dt); } -inline size_t TokenBoundary::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { - c.in_token = true; - auto se = make_scope_exit([&]() { c.in_token = false; }); - const auto& rule = *ope_; - auto len = rule.parse(s, n, sv, c, dt); - if (success(len)) { - sv.tokens.push_back(std::make_pair(s, len)); +inline void Context::trace_leave(const char *name, const char *a_s, size_t n, + SemanticValues &sv, any &dt, + size_t len) const { + tracer_leave(name, a_s, n, sv, *this, dt, len); + trace_ids.pop_back(); +} - if (c.whitespaceOpe) { - auto l = c.whitespaceOpe->parse(s + len, n - len, sv, c, dt); - if (fail(l)) { - return static_cast<size_t>(-1); - } - len += l; - } - } +inline bool Context::is_traceable(const Ope &ope) const { + if (tracer_enter && tracer_leave) { + IsReference vis; + const_cast<Ope &>(ope).accept(vis); + return !vis.is_reference; + } + return false; +} + +inline size_t Ope::parse(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt) const { + if (c.is_traceable(*this)) { + TraceOpeName vis; + const_cast<Ope &>(*this).accept(vis); + c.trace_enter(vis.name, s, n, sv, dt); + auto len = parse_core(s, n, sv, c, dt); + c.trace_leave(vis.name, s, n, sv, dt, len); return len; + } + return parse_core(s, n, sv, c, dt); } -inline size_t Holder::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { - if (!ope_) { - throw std::logic_error("Uninitialized definition ope was used..."); - } +inline size_t Dictionary::parse_core(const char *s, size_t n, + SemanticValues & /*sv*/, Context &c, + any & /*dt*/) const { + auto len = trie_.match(s, n); + if (len > 0) { return len; } + c.set_error_pos(s); + return static_cast<size_t>(-1); +} - c.trace(outer_->name.c_str(), s, n, sv, dt); - c.nest_level++; - auto se = make_scope_exit([&]() { - c.nest_level--; - }); +inline size_t LiteralString::parse_core(const char *s, size_t n, + SemanticValues &sv, Context &c, + any &dt) const { + return parse_literal(s, n, sv, c, dt, lit_, init_is_word_, is_word_, + ignore_case_); +} - // Macro reference - // TODO: need packrat support - if (outer_->is_macro) { - const auto& rule = *ope_; - return rule.parse(s, n, sv, c, dt); +inline size_t TokenBoundary::parse_core(const char *s, size_t n, + SemanticValues &sv, Context &c, + any &dt) const { + c.in_token = true; + auto se = make_scope_exit([&]() { c.in_token = false; }); + const auto &rule = *ope_; + auto len = rule.parse(s, n, sv, c, dt); + if (success(len)) { + sv.tokens.push_back(std::make_pair(s, len)); + + if (c.whitespaceOpe) { + auto l = c.whitespaceOpe->parse(s + len, n - len, sv, c, dt); + if (fail(l)) { return static_cast<size_t>(-1); } + len += l; } + } + return len; +} - size_t len; - any val; +inline size_t Holder::parse_core(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt) const { + if (!ope_) { + throw std::logic_error("Uninitialized definition ope was used..."); + } - c.packrat(s, outer_->id, len, val, [&](any& a_val) { - if (outer_->enter) { - outer_->enter(s, n, dt); - } + // Macro reference + // TODO: need packrat support + if (outer_->is_macro) { return ope_->parse(s, n, sv, c, dt); } - auto se2 = make_scope_exit([&]() { - c.pop(); + size_t len; + any val; - if (outer_->leave) { - outer_->leave(s, n, len, a_val, dt); - } - }); + c.packrat(s, outer_->id, len, val, [&](any &a_val) { + if (outer_->enter) { outer_->enter(s, n, dt); } - auto& chldsv = c.push(); + auto se2 = make_scope_exit([&]() { + c.pop(); - const auto& rule = *ope_; - len = rule.parse(s, n, chldsv, c, dt); + if (outer_->leave) { outer_->leave(s, n, len, a_val, dt); } + }); - // Invoke action - if (success(len)) { - chldsv.s_ = s; - chldsv.n_ = len; + auto &chldsv = c.push(); - try { - a_val = reduce(chldsv, dt); - } catch (const parse_error& e) { - if (e.what()) { - if (c.message_pos < s) { - c.message_pos = s; - c.message = e.what(); - } - } - len = static_cast<size_t>(-1); - } - } - }); + len = ope_->parse(s, n, chldsv, c, dt); + // Invoke action if (success(len)) { - if (!outer_->ignoreSemanticValue) { - sv.emplace_back(val); - } - } else { - if (outer_->error_message) { - if (c.message_pos < s) { - c.message_pos = s; - c.message = outer_->error_message(); - } + chldsv.s_ = s; + chldsv.n_ = len; + chldsv.name_ = outer_->name; + + if (!IsPrioritizedChoice::check(*ope_)) { + chldsv.choice_count_ = 0; + chldsv.choice_ = 0; + } + + try { + a_val = reduce(chldsv, dt); + } catch (const parse_error &e) { + if (e.what()) { + if (c.message_pos < s) { + c.message_pos = s; + c.message = e.what(); + } } + len = static_cast<size_t>(-1); + } } + }); - return len; + if (success(len)) { + if (!outer_->ignoreSemanticValue) { + sv.emplace_back(val); + sv.tags.emplace_back(str2tag(outer_->name.c_str())); + } + } else { + if (outer_->error_message) { + if (c.message_pos < s) { + c.message_pos = s; + c.message = outer_->error_message(); + } + } + } + + return len; } -inline any Holder::reduce(SemanticValues& sv, any& dt) const { - if (outer_->action) { - return outer_->action(sv, dt); - } else if (sv.empty()) { - return any(); - } else { - return std::move(sv.front()); - } +inline any Holder::reduce(SemanticValues &sv, any &dt) const { + if (outer_->action && !outer_->disable_action) { + return outer_->action(sv, dt); + } else if (sv.empty()) { + return any(); + } else { + return std::move(sv.front()); + } } -inline size_t Reference::parse( - const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { - if (rule_) { - // Reference rule - if (rule_->is_macro) { - // Macro - FindReference vis(c.top_args(), rule_->params); +inline const char *Holder::trace_name() const { + if (trace_name_.empty()) { trace_name_ = "[" + outer_->name + "]"; } + return trace_name_.c_str(); +} - // Collect arguments - std::vector<std::shared_ptr<Ope>> args; - for (auto arg: args_) { - arg->accept(vis); - args.push_back(vis.found_ope); - } +inline size_t Reference::parse_core(const char *s, size_t n, SemanticValues &sv, + Context &c, any &dt) const { + if (rule_) { + // Reference rule + if (rule_->is_macro) { + // Macro + FindReference vis(c.top_args(), rule_->params); - c.push_args(args); - auto se = make_scope_exit([&]() { c.pop_args(); }); - auto ope = get_core_operator(); - return ope->parse(s, n, sv, c, dt); - } else { - // Definition - auto ope = get_core_operator(); - return ope->parse(s, n, sv, c, dt); - } + // Collect arguments + std::vector<std::shared_ptr<Ope>> args; + for (auto arg : args_) { + arg->accept(vis); + args.push_back(vis.found_ope); + } + + c.push_args(std::move(args)); + auto se = make_scope_exit([&]() { c.pop_args(); }); + auto ope = get_core_operator(); + return ope->parse(s, n, sv, c, dt); } else { - // Reference parameter in macro - const auto& args = c.top_args(); - return args[iarg_]->parse(s, n, sv, c, dt); - } + // Definition + auto ope = get_core_operator(); + return ope->parse(s, n, sv, c, dt); + } + } else { + // Reference parameter in macro + const auto &args = c.top_args(); + return args[iarg_]->parse(s, n, sv, c, dt); + } } inline std::shared_ptr<Ope> Reference::get_core_operator() const { - return rule_->holder_; -} - -inline size_t BackReference::parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const { - c.trace("BackReference", s, n, sv, dt); - auto it = c.capture_scope_stack.rbegin(); - while (it != c.capture_scope_stack.rend()) { - const auto& captures = *it; - if (captures.find(name_) != captures.end()) { - const auto& lit = captures.at(name_); - auto init_is_word = false; - auto is_word = false; - return parse_literal(s, n, sv, c, dt, lit, init_is_word, is_word); - } - ++it; - } - throw std::runtime_error("Invalid back reference..."); -} - -inline void Sequence::accept(Visitor& v) { v.visit(*this); } -inline void PrioritizedChoice::accept(Visitor& v) { v.visit(*this); } -inline void ZeroOrMore::accept(Visitor& v) { v.visit(*this); } -inline void OneOrMore::accept(Visitor& v) { v.visit(*this); } -inline void Option::accept(Visitor& v) { v.visit(*this); } -inline void AndPredicate::accept(Visitor& v) { v.visit(*this); } -inline void NotPredicate::accept(Visitor& v) { v.visit(*this); } -inline void LiteralString::accept(Visitor& v) { v.visit(*this); } -inline void CharacterClass::accept(Visitor& v) { v.visit(*this); } -inline void Character::accept(Visitor& v) { v.visit(*this); } -inline void AnyCharacter::accept(Visitor& v) { v.visit(*this); } -inline void CaptureScope::accept(Visitor& v) { v.visit(*this); } -inline void Capture::accept(Visitor& v) { v.visit(*this); } -inline void TokenBoundary::accept(Visitor& v) { v.visit(*this); } -inline void Ignore::accept(Visitor& v) { v.visit(*this); } -inline void User::accept(Visitor& v) { v.visit(*this); } -inline void WeakHolder::accept(Visitor& v) { v.visit(*this); } -inline void Holder::accept(Visitor& v) { v.visit(*this); } -inline void Reference::accept(Visitor& v) { v.visit(*this); } -inline void Whitespace::accept(Visitor& v) { v.visit(*this); } -inline void BackReference::accept(Visitor& v) { v.visit(*this); } - -inline void AssignIDToDefinition::visit(Holder& ope) { - auto p = static_cast<void*>(ope.outer_); - if (ids.count(p)) { - return; + return rule_->holder_; +} + +inline size_t BackReference::parse_core(const char *s, size_t n, + SemanticValues &sv, Context &c, + any &dt) const { + auto size = static_cast<int>(c.capture_scope_stack_size); + for (auto i = size - 1; i >= 0; i--) { + auto index = static_cast<size_t>(i); + const auto &cs = c.capture_scope_stack[index]; + if (cs.find(name_) != cs.end()) { + const auto &lit = cs.at(name_); + auto init_is_word = false; + auto is_word = false; + return parse_literal(s, n, sv, c, dt, lit, init_is_word, is_word, false); } - auto id = ids.size(); - ids[p] = id; - ope.outer_->id = id; - ope.ope_->accept(*this); + } + throw std::runtime_error("Invalid back reference..."); } -inline void AssignIDToDefinition::visit(Reference& ope) { - if (ope.rule_) { - for (auto arg: ope.args_) { - arg->accept(*this); - } - ope.rule_->accept(*this); - } -} +inline Definition & +PrecedenceClimbing::get_reference_for_binop(Context &c) const { + if (rule_.is_macro) { + // Reference parameter in macro + const auto &args = c.top_args(); + auto iarg = dynamic_cast<Reference &>(*binop_).iarg_; + auto arg = args[iarg]; + return *dynamic_cast<Reference &>(*arg).rule_; + } -inline void TokenChecker::visit(Reference& ope) { - if (ope.is_macro_) { - ope.rule_->accept(*this); - for (auto arg: ope.args_) { - arg->accept(*this); - } - } else { - has_rule_ = true; - } + return *dynamic_cast<Reference &>(*binop_).rule_; } -inline void DetectLeftRecursion::visit(Reference& ope) { - if (ope.name_ == name_) { - error_s = ope.s_; - } else if (!refs_.count(ope.name_)) { - refs_.insert(ope.name_); - if (ope.rule_) { - ope.rule_->accept(*this); - } - } - done_ = true; -} +inline size_t PrecedenceClimbing::parse_expression(const char *s, size_t n, + SemanticValues &sv, + Context &c, any &dt, + size_t min_prec) const { + auto len = atom_->parse(s, n, sv, c, dt); + if (fail(len)) { return len; } -inline void ReferenceChecker::visit(Reference& ope) { - auto it = std::find(params_.begin(), params_.end(), ope.name_); - if (it != params_.end()) { - return; + std::string tok; + auto &rule = get_reference_for_binop(c); + auto action = rule.action; + + rule.action = [&](SemanticValues &sv2, any &dt2) -> any { + tok = sv2.token(); + if (action) { + return action(sv2, dt2); + } else if (!sv2.empty()) { + return sv2[0]; } + return any(); + }; + auto action_se = make_scope_exit([&]() { rule.action = action; }); - if (!grammar_.count(ope.name_)) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "'" + ope.name_ + "' is not defined."; - } else { - const auto& rule = grammar_.at(ope.name_); - if (rule.is_macro) { - if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "incorrect number of arguments."; - } - } else if (ope.is_macro_) { - error_s[ope.name_] = ope.s_; - error_message[ope.name_] = "'" + ope.name_ + "' is not macro."; - } + auto save_error_pos = c.error_pos; + + auto i = len; + while (i < n) { + std::vector<any> save_values(sv.begin(), sv.end()); + auto save_tokens = sv.tokens; + + auto chv = c.push(); + auto chl = binop_->parse(s + i, n - i, chv, c, dt); + c.pop(); + + if (fail(chl)) { + c.error_pos = save_error_pos; + break; } -} -inline void LinkReferences::visit(Reference& ope) { - if (grammar_.count(ope.name_)) { - auto& rule = grammar_.at(ope.name_); - ope.rule_ = &rule; - } else { - for (size_t i = 0; i < params_.size(); i++) { - const auto& param = params_[i]; - if (param == ope.name_) { - ope.iarg_ = i; - break; - } - } + auto it = info_.find(tok); + if (it == info_.end()) { break; } + + auto level = std::get<0>(it->second); + auto assoc = std::get<1>(it->second); + + if (level < min_prec) { break; } + + sv.emplace_back(std::move(chv[0])); + i += chl; + + auto next_min_prec = level; + if (assoc == 'L') { next_min_prec = level + 1; } + + chv = c.push(); + chl = parse_expression(s + i, n - i, chv, c, dt, next_min_prec); + c.pop(); + + if (fail(chl)) { + sv.assign(save_values.begin(), save_values.end()); + sv.tokens = save_tokens; + c.error_pos = save_error_pos; + break; } - for (auto arg: ope.args_) { - arg->accept(*this); + + sv.emplace_back(std::move(chv[0])); + i += chl; + + any val; + if (rule_.action) { + sv.s_ = s; + sv.n_ = i; + val = rule_.action(sv, dt); + } else if (!sv.empty()) { + val = sv[0]; } + sv.clear(); + sv.emplace_back(std::move(val)); + } + + return i; +} + +inline void Sequence::accept(Visitor &v) { v.visit(*this); } +inline void PrioritizedChoice::accept(Visitor &v) { v.visit(*this); } +inline void Repetition::accept(Visitor &v) { v.visit(*this); } +inline void AndPredicate::accept(Visitor &v) { v.visit(*this); } +inline void NotPredicate::accept(Visitor &v) { v.visit(*this); } +inline void Dictionary::accept(Visitor &v) { v.visit(*this); } +inline void LiteralString::accept(Visitor &v) { v.visit(*this); } +inline void CharacterClass::accept(Visitor &v) { v.visit(*this); } +inline void Character::accept(Visitor &v) { v.visit(*this); } +inline void AnyCharacter::accept(Visitor &v) { v.visit(*this); } +inline void CaptureScope::accept(Visitor &v) { v.visit(*this); } +inline void Capture::accept(Visitor &v) { v.visit(*this); } +inline void TokenBoundary::accept(Visitor &v) { v.visit(*this); } +inline void Ignore::accept(Visitor &v) { v.visit(*this); } +inline void User::accept(Visitor &v) { v.visit(*this); } +inline void WeakHolder::accept(Visitor &v) { v.visit(*this); } +inline void Holder::accept(Visitor &v) { v.visit(*this); } +inline void Reference::accept(Visitor &v) { v.visit(*this); } +inline void Whitespace::accept(Visitor &v) { v.visit(*this); } +inline void BackReference::accept(Visitor &v) { v.visit(*this); } +inline void PrecedenceClimbing::accept(Visitor &v) { v.visit(*this); } + +inline void AssignIDToDefinition::visit(Holder &ope) { + auto p = static_cast<void *>(ope.outer_); + if (ids.count(p)) { return; } + auto id = ids.size(); + ids[p] = id; + ope.outer_->id = id; + ope.ope_->accept(*this); +} + +inline void AssignIDToDefinition::visit(Reference &ope) { + if (ope.rule_) { + for (auto arg : ope.args_) { + arg->accept(*this); + } + ope.rule_->accept(*this); + } } -inline void FindReference::visit(Reference& ope) { - for (size_t i = 0; i < args_.size(); i++) { - const auto& name = params_[i]; - if (name == ope.name_) { - found_ope = args_[i]; - return; - } +inline void TokenChecker::visit(Reference &ope) { + if (ope.is_macro_) { + ope.rule_->accept(*this); + for (auto arg : ope.args_) { + arg->accept(*this); } - found_ope = ope.shared_from_this(); + } else { + has_rule_ = true; + } } -/*----------------------------------------------------------------------------- - * PEG parser generator - *---------------------------------------------------------------------------*/ +inline void DetectLeftRecursion::visit(Reference &ope) { + if (ope.name_ == name_) { + error_s = ope.s_; + } else if (!refs_.count(ope.name_)) { + refs_.insert(ope.name_); + if (ope.rule_) { + ope.rule_->accept(*this); + if (done_ == false) { return; } + } + } + done_ = true; +} -typedef std::unordered_map<std::string, std::shared_ptr<Ope>> Rules; -typedef std::function<void (size_t, size_t, const std::string&)> Log; +inline void HasEmptyElement::visit(Reference &ope) { + auto it = std::find_if(refs_.begin(), refs_.end(), + [&](const std::pair<const char *, std::string> &ref) { + return ope.name_ == ref.second; + }); + if (it != refs_.end()) { return; } -class ParserGenerator -{ -public: - static std::shared_ptr<Grammar> parse( - const char* s, - size_t n, - const Rules& rules, - std::string& start, - Log log) - { - return get_instance().perform_core(s, n, rules, start, log); - } + if (ope.rule_) { + refs_.emplace_back(ope.s_, ope.name_); + ope.rule_->accept(*this); + refs_.pop_back(); + } +} - static std::shared_ptr<Grammar> parse( - const char* s, - size_t n, - std::string& start, - Log log) - { - Rules dummy; - return parse(s, n, dummy, start, log); - } +inline void DetectInfiniteLoop::visit(Reference &ope) { + auto it = std::find_if(refs_.begin(), refs_.end(), + [&](const std::pair<const char *, std::string> &ref) { + return ope.name_ == ref.second; + }); + if (it != refs_.end()) { return; } - // For debuging purpose - static Grammar& grammar() { - return get_instance().g; - } + if (ope.rule_) { + refs_.emplace_back(ope.s_, ope.name_); + ope.rule_->accept(*this); + refs_.pop_back(); + } +} -private: - static ParserGenerator& get_instance() { - static ParserGenerator instance; - return instance; +inline void ReferenceChecker::visit(Reference &ope) { + auto it = std::find(params_.begin(), params_.end(), ope.name_); + if (it != params_.end()) { return; } + + if (!grammar_.count(ope.name_)) { + error_s[ope.name_] = ope.s_; + error_message[ope.name_] = "'" + ope.name_ + "' is not defined."; + } else { + const auto &rule = grammar_.at(ope.name_); + if (rule.is_macro) { + if (!ope.is_macro_ || ope.args_.size() != rule.params.size()) { + error_s[ope.name_] = ope.s_; + error_message[ope.name_] = "incorrect number of arguments."; + } + } else if (ope.is_macro_) { + error_s[ope.name_] = ope.s_; + error_message[ope.name_] = "'" + ope.name_ + "' is not macro."; } + } +} - ParserGenerator() { - make_grammar(); - setup_actions(); +inline void LinkReferences::visit(Reference &ope) { + // Check if the reference is a macro parameter + auto found_param = false; + for (size_t i = 0; i < params_.size(); i++) { + const auto ¶m = params_[i]; + if (param == ope.name_) { + ope.iarg_ = i; + found_param = true; + break; } + } - struct Data { - std::shared_ptr<Grammar> grammar; - std::string start; - std::vector<std::pair<std::string, const char*>> duplicates; + // Check if the reference is a definition rule + if (!found_param && grammar_.count(ope.name_)) { + auto &rule = grammar_.at(ope.name_); + ope.rule_ = &rule; + } - Data(): grammar(std::make_shared<Grammar>()) {} - }; + for (auto arg : ope.args_) { + arg->accept(*this); + } +} - void make_grammar() { - // Setup PEG syntax parser - g["Grammar"] <= seq(g["Spacing"], oom(g["Definition"]), g["EndOfFile"]); - g["Definition"] <= cho(seq(g["Ignore"], g["IdentCont"], g["Parameters"], g["LEFTARROW"], g["Expression"]), - seq(g["Ignore"], g["Identifier"], g["LEFTARROW"], g["Expression"])); - - g["Expression"] <= seq(g["Sequence"], zom(seq(g["SLASH"], g["Sequence"]))); - g["Sequence"] <= zom(g["Prefix"]); - g["Prefix"] <= seq(opt(cho(g["AND"], g["NOT"])), g["Suffix"]); - g["Suffix"] <= seq(g["Primary"], opt(cho(g["QUESTION"], g["STAR"], g["PLUS"]))); - g["Primary"] <= cho(seq(g["Ignore"], g["IdentCont"], g["Arguments"], npd(g["LEFTARROW"])), - seq(g["Ignore"], g["Identifier"], npd(seq(opt(g["Parameters"]), g["LEFTARROW"]))), - seq(g["OPEN"], g["Expression"], g["CLOSE"]), - seq(g["BeginTok"], g["Expression"], g["EndTok"]), - seq(g["BeginCapScope"], g["Expression"], g["EndCapScope"]), - seq(g["BeginCap"], g["Expression"], g["EndCap"]), - g["BackRef"], g["Literal"], g["Class"], g["DOT"]); - - g["Identifier"] <= seq(g["IdentCont"], g["Spacing"]); - g["IdentCont"] <= seq(g["IdentStart"], zom(g["IdentRest"])); - - const static std::vector<std::pair<char32_t, char32_t>> range = {{ 0x0080, 0xFFFF }}; - g["IdentStart"] <= cho(cls("a-zA-Z_%"), cls(range)); - - g["IdentRest"] <= cho(g["IdentStart"], cls("0-9")); - - g["Literal"] <= cho(seq(cls("'"), tok(zom(seq(npd(cls("'")), g["Char"]))), cls("'"), g["Spacing"]), - seq(cls("\""), tok(zom(seq(npd(cls("\"")), g["Char"]))), cls("\""), g["Spacing"])); - - g["Class"] <= seq(chr('['), tok(zom(seq(npd(chr(']')), g["Range"]))), chr(']'), g["Spacing"]); - - g["Range"] <= cho(seq(g["Char"], chr('-'), g["Char"]), g["Char"]); - g["Char"] <= cho(seq(chr('\\'), cls("nrt'\"[]\\")), - seq(chr('\\'), cls("0-3"), cls("0-7"), cls("0-7")), - seq(chr('\\'), cls("0-7"), opt(cls("0-7"))), - seq(lit("\\x"), cls("0-9a-fA-F"), opt(cls("0-9a-fA-F"))), - seq(lit("\\u"), cls("0-9a-fA-F"), cls("0-9a-fA-F"), cls("0-9a-fA-F"), cls("0-9a-fA-F")), - seq(npd(chr('\\')), dot())); - -#if defined(PEGLIB_NO_UNICODE_CHARS) - g["LEFTARROW"] <= seq(lit("<-"), g["Spacing"]); -#else - g["LEFTARROW"] <= seq(cho(lit("<-"), lit(u8"←")), g["Spacing"]); -#endif - ~g["SLASH"] <= seq(chr('/'), g["Spacing"]); - g["AND"] <= seq(chr('&'), g["Spacing"]); - g["NOT"] <= seq(chr('!'), g["Spacing"]); - g["QUESTION"] <= seq(chr('?'), g["Spacing"]); - g["STAR"] <= seq(chr('*'), g["Spacing"]); - g["PLUS"] <= seq(chr('+'), g["Spacing"]); - ~g["OPEN"] <= seq(chr('('), g["Spacing"]); - ~g["CLOSE"] <= seq(chr(')'), g["Spacing"]); - g["DOT"] <= seq(chr('.'), g["Spacing"]); +inline void FindReference::visit(Reference &ope) { + for (size_t i = 0; i < args_.size(); i++) { + const auto &name = params_[i]; + if (name == ope.name_) { + found_ope = args_[i]; + return; + } + } + found_ope = ope.shared_from_this(); +} - ~g["Spacing"] <= zom(cho(g["Space"], g["Comment"])); - g["Comment"] <= seq(chr('#'), zom(seq(npd(g["EndOfLine"]), dot())), g["EndOfLine"]); - g["Space"] <= cho(chr(' '), chr('\t'), g["EndOfLine"]); - g["EndOfLine"] <= cho(lit("\r\n"), chr('\n'), chr('\r')); - g["EndOfFile"] <= npd(dot()); +/*----------------------------------------------------------------------------- + * PEG parser generator + *---------------------------------------------------------------------------*/ + +typedef std::unordered_map<std::string, std::shared_ptr<Ope>> Rules; +typedef std::function<void(size_t, size_t, const std::string &)> Log; + +class ParserGenerator { +public: + static std::shared_ptr<Grammar> parse(const char *s, size_t n, + const Rules &rules, std::string &start, + Log log) { + return get_instance().perform_core(s, n, rules, start, log); + } - ~g["BeginTok"] <= seq(chr('<'), g["Spacing"]); - ~g["EndTok"] <= seq(chr('>'), g["Spacing"]); + static std::shared_ptr<Grammar> parse(const char *s, size_t n, + std::string &start, Log log) { + Rules dummy; + return parse(s, n, dummy, start, log); + } - ~g["BeginCapScope"] <= seq(chr('$'), chr('('), g["Spacing"]); - ~g["EndCapScope"] <= seq(chr(')'), g["Spacing"]); + // For debuging purpose + static Grammar &grammar() { return get_instance().g; } - g["BeginCap"] <= seq(chr('$'), tok(g["IdentCont"]), chr('<'), g["Spacing"]); - ~g["EndCap"] <= seq(chr('>'), g["Spacing"]); +private: + static ParserGenerator &get_instance() { + static ParserGenerator instance; + return instance; + } - g["BackRef"] <= seq(chr('$'), tok(g["IdentCont"]), g["Spacing"]); + ParserGenerator() { + make_grammar(); + setup_actions(); + } - g["IGNORE"] <= chr('~'); + struct Instruction { + std::string type; + any data; + }; + + struct Data { + std::shared_ptr<Grammar> grammar; + std::string start; + const char *start_pos = nullptr; + std::vector<std::pair<std::string, const char *>> duplicates; + std::map<std::string, Instruction> instructions; + + Data() : grammar(std::make_shared<Grammar>()) {} + }; + + void make_grammar() { + // Setup PEG syntax parser + g["Grammar"] <= seq(g["Spacing"], oom(g["Definition"]), g["EndOfFile"]); + g["Definition"] <= + cho(seq(g["Ignore"], g["IdentCont"], g["Parameters"], g["LEFTARROW"], + g["Expression"], opt(g["Instruction"])), + seq(g["Ignore"], g["Identifier"], g["LEFTARROW"], g["Expression"], + opt(g["Instruction"]))); + g["Expression"] <= seq(g["Sequence"], zom(seq(g["SLASH"], g["Sequence"]))); + g["Sequence"] <= zom(g["Prefix"]); + g["Prefix"] <= seq(opt(cho(g["AND"], g["NOT"])), g["Suffix"]); + g["Suffix"] <= seq(g["Primary"], opt(g["Loop"])); + g["Loop"] <= cho(g["QUESTION"], g["STAR"], g["PLUS"], g["Repetition"]); + g["Primary"] <= + cho(seq(g["Ignore"], g["IdentCont"], g["Arguments"], + npd(g["LEFTARROW"])), + seq(g["Ignore"], g["Identifier"], + npd(seq(opt(g["Parameters"]), g["LEFTARROW"]))), + seq(g["OPEN"], g["Expression"], g["CLOSE"]), + seq(g["BeginTok"], g["Expression"], g["EndTok"]), + seq(g["BeginCapScope"], g["Expression"], g["EndCapScope"]), + seq(g["BeginCap"], g["Expression"], g["EndCap"]), g["BackRef"], + g["LiteralI"], g["Dictionary"], g["Literal"], g["NegatedClass"], + g["Class"], g["DOT"]); + + g["Identifier"] <= seq(g["IdentCont"], g["Spacing"]); + g["IdentCont"] <= seq(g["IdentStart"], zom(g["IdentRest"])); + + const static std::vector<std::pair<char32_t, char32_t>> range = { + {0x0080, 0xFFFF}}; + g["IdentStart"] <= cho(cls("a-zA-Z_%"), cls(range)); + + g["IdentRest"] <= cho(g["IdentStart"], cls("0-9")); + + g["Dictionary"] <= seq(g["LiteralD"], oom(seq(g["PIPE"], g["LiteralD"]))); + + auto lit_ope = cho(seq(cls("'"), tok(zom(seq(npd(cls("'")), g["Char"]))), + cls("'"), g["Spacing"]), + seq(cls("\""), tok(zom(seq(npd(cls("\"")), g["Char"]))), + cls("\""), g["Spacing"])); + g["Literal"] <= lit_ope; + g["LiteralD"] <= lit_ope; + + g["LiteralI"] <= + cho(seq(cls("'"), tok(zom(seq(npd(cls("'")), g["Char"]))), lit("'i"), + g["Spacing"]), + seq(cls("\""), tok(zom(seq(npd(cls("\"")), g["Char"]))), lit("\"i"), + g["Spacing"])); + + // NOTE: The original Brian Ford's paper uses 'zom' instead of 'oom'. + g["Class"] <= seq(chr('['), npd(chr('^')), + tok(oom(seq(npd(chr(']')), g["Range"]))), chr(']'), + g["Spacing"]); + g["NegatedClass"] <= seq(lit("[^"), + tok(oom(seq(npd(chr(']')), g["Range"]))), chr(']'), + g["Spacing"]); + + g["Range"] <= cho(seq(g["Char"], chr('-'), g["Char"]), g["Char"]); + g["Char"] <= cho(seq(chr('\\'), cls("nrt'\"[]\\^")), + seq(chr('\\'), cls("0-3"), cls("0-7"), cls("0-7")), + seq(chr('\\'), cls("0-7"), opt(cls("0-7"))), + seq(lit("\\x"), cls("0-9a-fA-F"), opt(cls("0-9a-fA-F"))), + seq(lit("\\u"), cls("0-9a-fA-F"), cls("0-9a-fA-F"), + cls("0-9a-fA-F"), cls("0-9a-fA-F")), + seq(npd(chr('\\')), dot())); + + g["Repetition"] <= + seq(g["BeginBlacket"], g["RepetitionRange"], g["EndBlacket"]); + g["RepetitionRange"] <= cho(seq(g["Number"], g["COMMA"], g["Number"]), + seq(g["Number"], g["COMMA"]), g["Number"], + seq(g["COMMA"], g["Number"])); + g["Number"] <= seq(oom(cls("0-9")), g["Spacing"]); + + g["LEFTARROW"] <= + seq(cho(lit("<-"), lit(reinterpret_cast<const char *>(u8"←"))), + g["Spacing"]); + ~g["SLASH"] <= seq(chr('/'), g["Spacing"]); + ~g["PIPE"] <= seq(chr('|'), g["Spacing"]); + g["AND"] <= seq(chr('&'), g["Spacing"]); + g["NOT"] <= seq(chr('!'), g["Spacing"]); + g["QUESTION"] <= seq(chr('?'), g["Spacing"]); + g["STAR"] <= seq(chr('*'), g["Spacing"]); + g["PLUS"] <= seq(chr('+'), g["Spacing"]); + ~g["OPEN"] <= seq(chr('('), g["Spacing"]); + ~g["CLOSE"] <= seq(chr(')'), g["Spacing"]); + g["DOT"] <= seq(chr('.'), g["Spacing"]); + + ~g["Spacing"] <= zom(cho(g["Space"], g["Comment"])); + g["Comment"] <= + seq(chr('#'), zom(seq(npd(g["EndOfLine"]), dot())), g["EndOfLine"]); + g["Space"] <= cho(chr(' '), chr('\t'), g["EndOfLine"]); + g["EndOfLine"] <= cho(lit("\r\n"), chr('\n'), chr('\r')); + g["EndOfFile"] <= npd(dot()); + + ~g["BeginTok"] <= seq(chr('<'), g["Spacing"]); + ~g["EndTok"] <= seq(chr('>'), g["Spacing"]); + + ~g["BeginCapScope"] <= seq(chr('$'), chr('('), g["Spacing"]); + ~g["EndCapScope"] <= seq(chr(')'), g["Spacing"]); + + g["BeginCap"] <= seq(chr('$'), tok(g["IdentCont"]), chr('<'), g["Spacing"]); + ~g["EndCap"] <= seq(chr('>'), g["Spacing"]); + + g["BackRef"] <= seq(chr('$'), tok(g["IdentCont"]), g["Spacing"]); + + g["IGNORE"] <= chr('~'); + + g["Ignore"] <= opt(g["IGNORE"]); + g["Parameters"] <= seq(g["OPEN"], g["Identifier"], + zom(seq(g["COMMA"], g["Identifier"])), g["CLOSE"]); + g["Arguments"] <= seq(g["OPEN"], g["Expression"], + zom(seq(g["COMMA"], g["Expression"])), g["CLOSE"]); + ~g["COMMA"] <= seq(chr(','), g["Spacing"]); + + // Instruction grammars + g["Instruction"] <= + seq(g["BeginBlacket"], cho(g["PrecedenceClimbing"]), g["EndBlacket"]); + + ~g["SpacesZom"] <= zom(g["Space"]); + ~g["SpacesOom"] <= oom(g["Space"]); + ~g["BeginBlacket"] <= seq(chr('{'), g["Spacing"]); + ~g["EndBlacket"] <= seq(chr('}'), g["Spacing"]); + + // PrecedenceClimbing instruction + g["PrecedenceClimbing"] <= + seq(lit("precedence"), g["SpacesZom"], g["PrecedenceInfo"], + zom(seq(g["SpacesOom"], g["PrecedenceInfo"])), g["SpacesZom"]); + g["PrecedenceInfo"] <= + seq(g["PrecedenceAssoc"], + oom(seq(ign(g["SpacesOom"]), g["PrecedenceOpe"]))); + g["PrecedenceOpe"] <= + tok(oom( + seq(npd(cho(g["PrecedenceAssoc"], g["Space"], chr('}'))), dot()))); + g["PrecedenceAssoc"] <= cls("LR"); + + // Set definition names + for (auto &x : g) { + x.second.name = x.first; + } + } - g["Ignore"] <= opt(g["IGNORE"]); - g["Parameters"] <= seq(g["OPEN"], g["Identifier"], zom(seq(g["COMMA"], g["Identifier"])), g["CLOSE"]); - g["Arguments"] <= seq(g["OPEN"], g["Expression"], zom(seq(g["COMMA"], g["Expression"])), g["CLOSE"]); - ~g["COMMA"] <= seq(chr(','), g["Spacing"]); + void setup_actions() { + g["Definition"] = [&](const SemanticValues &sv, any &dt) { + Data &data = *any_cast<Data *>(dt); + auto is_macro = sv.choice() == 0; + auto ignore = any_cast<bool>(sv[0]); + auto name = any_cast<std::string>(sv[1]); - // Set definition names - for (auto& x: g) { - x.second.name = x.first; + std::vector<std::string> params; + std::shared_ptr<Ope> ope; + if (is_macro) { + params = any_cast<std::vector<std::string>>(sv[2]); + ope = any_cast<std::shared_ptr<Ope>>(sv[4]); + if (sv.size() == 6) { + data.instructions[name] = any_cast<Instruction>(sv[5]); } - } + } else { + ope = any_cast<std::shared_ptr<Ope>>(sv[3]); + if (sv.size() == 5) { + data.instructions[name] = any_cast<Instruction>(sv[4]); + } + } - void setup_actions() { - g["Definition"] = [&](const SemanticValues& sv, any& dt) { - auto is_macro = sv.choice() == 0; - auto ignore = sv[0].get<bool>(); - auto name = sv[1].get<std::string>(); + auto &grammar = *data.grammar; + if (!grammar.count(name)) { + auto &rule = grammar[name]; + rule <= ope; + rule.name = name; + rule.s_ = sv.c_str(); + rule.ignoreSemanticValue = ignore; + rule.is_macro = is_macro; + rule.params = params; + + if (data.start.empty()) { + data.start = name; + data.start_pos = sv.c_str(); + } + } else { + data.duplicates.emplace_back(name, sv.c_str()); + } + }; - std::vector<std::string> params; - std::shared_ptr<Ope> ope; - if (is_macro) { - params = sv[2].get<std::vector<std::string>>(); - ope = sv[4].get<std::shared_ptr<Ope>>(); - } else { - ope = sv[3].get<std::shared_ptr<Ope>>(); - } + g["Expression"] = [&](const SemanticValues &sv) { + if (sv.size() == 1) { + return any_cast<std::shared_ptr<Ope>>(sv[0]); + } else { + std::vector<std::shared_ptr<Ope>> opes; + for (auto i = 0u; i < sv.size(); i++) { + opes.emplace_back(any_cast<std::shared_ptr<Ope>>(sv[i])); + } + const std::shared_ptr<Ope> ope = + std::make_shared<PrioritizedChoice>(opes); + return ope; + } + }; - Data& data = *dt.get<Data*>(); + g["Sequence"] = [&](const SemanticValues &sv) { + if (sv.size() == 1) { + return any_cast<std::shared_ptr<Ope>>(sv[0]); + } else { + std::vector<std::shared_ptr<Ope>> opes; + for (const auto &x : sv) { + opes.emplace_back(any_cast<std::shared_ptr<Ope>>(x)); + } + const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes); + return ope; + } + }; - auto& grammar = *data.grammar; - if (!grammar.count(name)) { - auto& rule = grammar[name]; - rule <= ope; - rule.name = name; - rule.ignoreSemanticValue = ignore; - rule.is_macro = is_macro; - rule.params = params; + g["Prefix"] = [&](const SemanticValues &sv) { + std::shared_ptr<Ope> ope; + if (sv.size() == 1) { + ope = any_cast<std::shared_ptr<Ope>>(sv[0]); + } else { + assert(sv.size() == 2); + auto tok = any_cast<char>(sv[0]); + ope = any_cast<std::shared_ptr<Ope>>(sv[1]); + if (tok == '&') { + ope = apd(ope); + } else { // '!' + ope = npd(ope); + } + } + return ope; + }; - if (data.start.empty()) { - data.start = name; - } - } else { - data.duplicates.emplace_back(name, sv.c_str()); - } - }; + struct Loop { + enum class Type { opt = 0, zom, oom, rep }; + Type type; + std::pair<size_t, size_t> range; + }; - g["Expression"] = [&](const SemanticValues& sv) { - if (sv.size() == 1) { - return sv[0].get<std::shared_ptr<Ope>>(); - } else { - std::vector<std::shared_ptr<Ope>> opes; - for (auto i = 0u; i < sv.size(); i++) { - opes.emplace_back(sv[i].get<std::shared_ptr<Ope>>()); - } - const std::shared_ptr<Ope> ope = std::make_shared<PrioritizedChoice>(opes); - return ope; - } - }; + g["Suffix"] = [&](const SemanticValues &sv) { + auto ope = any_cast<std::shared_ptr<Ope>>(sv[0]); + if (sv.size() == 1) { + return ope; + } else { + assert(sv.size() == 2); + auto loop = any_cast<Loop>(sv[1]); + switch (loop.type) { + case Loop::Type::opt: return opt(ope); + case Loop::Type::zom: return zom(ope); + case Loop::Type::oom: return oom(ope); + case Loop::Type::rep: // Regex-like repetition + return rep(ope, loop.range.first, loop.range.second); + } + } + }; - g["Sequence"] = [&](const SemanticValues& sv) { - if (sv.size() == 1) { - return sv[0].get<std::shared_ptr<Ope>>(); - } else { - std::vector<std::shared_ptr<Ope>> opes; - for (const auto& x: sv) { - opes.emplace_back(x.get<std::shared_ptr<Ope>>()); - } - const std::shared_ptr<Ope> ope = std::make_shared<Sequence>(opes); - return ope; - } - }; + g["Loop"] = [&](const SemanticValues &sv) { + switch (sv.choice()) { + case 0: // Option + return Loop{Loop::Type::opt, std::pair<size_t, size_t>()}; + case 1: // Zero or More + return Loop{Loop::Type::zom, std::pair<size_t, size_t>()}; + case 2: // One or More + return Loop{Loop::Type::oom, std::pair<size_t, size_t>()}; + default: // Regex-like repetition + return Loop{Loop::Type::rep, + any_cast<std::pair<size_t, size_t>>(sv[0])}; + } + }; - g["Prefix"] = [&](const SemanticValues& sv) { - std::shared_ptr<Ope> ope; - if (sv.size() == 1) { - ope = sv[0].get<std::shared_ptr<Ope>>(); - } else { - assert(sv.size() == 2); - auto tok = sv[0].get<char>(); - ope = sv[1].get<std::shared_ptr<Ope>>(); - if (tok == '&') { - ope = apd(ope); - } else { // '!' - ope = npd(ope); - } - } - return ope; - }; + g["RepetitionRange"] = [&](const SemanticValues &sv) { + switch (sv.choice()) { + case 0: { // Number COMMA Number + auto min = any_cast<size_t>(sv[0]); + auto max = any_cast<size_t>(sv[1]); + return std::make_pair(min, max); + } + case 1: // Number COMMA + return std::make_pair(any_cast<size_t>(sv[0]), + std::numeric_limits<size_t>::max()); + case 2: { // Number + auto n = any_cast<size_t>(sv[0]); + return std::make_pair(n, n); + } + default: // COMMA Number + return std::make_pair(std::numeric_limits<size_t>::min(), + any_cast<size_t>(sv[0])); + } + }; + g["Number"] = [&](const SemanticValues &sv) { + std::stringstream ss(sv.str()); + size_t n; + ss >> n; + return n; + }; - g["Suffix"] = [&](const SemanticValues& sv) { - auto ope = sv[0].get<std::shared_ptr<Ope>>(); - if (sv.size() == 1) { - return ope; - } else { - assert(sv.size() == 2); - auto tok = sv[1].get<char>(); - if (tok == '?') { - return opt(ope); - } else if (tok == '*') { - return zom(ope); - } else { // '+' - return oom(ope); - } - } - }; + g["Primary"] = [&](const SemanticValues &sv, any &dt) { + Data &data = *any_cast<Data *>(dt); - g["Primary"] = [&](const SemanticValues& sv, any& dt) -> std::shared_ptr<Ope> { - Data& data = *dt.get<Data*>(); + switch (sv.choice()) { + case 0: // Macro Reference + case 1: { // Reference + auto is_macro = sv.choice() == 0; + auto ignore = any_cast<bool>(sv[0]); + const auto &ident = any_cast<std::string>(sv[1]); - switch (sv.choice()) { - case 0: // Macro Reference - case 1: { // Reference - auto is_macro = sv.choice() == 0; - auto ignore = sv[0].get<bool>(); - const auto& ident = sv[1].get<std::string>(); + std::vector<std::shared_ptr<Ope>> args; + if (is_macro) { + args = any_cast<std::vector<std::shared_ptr<Ope>>>(sv[2]); + } - std::vector<std::shared_ptr<Ope>> args; - if (is_macro) { - args = sv[2].get<std::vector<std::shared_ptr<Ope>>>(); - } + std::shared_ptr<Ope> ope = + ref(*data.grammar, ident, sv.c_str(), is_macro, args); - if (ignore) { - return ign(ref(*data.grammar, ident, sv.c_str(), is_macro, args)); - } else { - return ref(*data.grammar, ident, sv.c_str(), is_macro, args); - } - } - case 2: { // (Expression) - return sv[0].get<std::shared_ptr<Ope>>(); - } - case 3: { // TokenBoundary - return tok(sv[0].get<std::shared_ptr<Ope>>()); - } - case 4: { // CaptureScope - return csc(sv[0].get<std::shared_ptr<Ope>>()); - } - case 5: { // Capture - const auto& name = sv[0].get<std::string>(); - auto ope = sv[1].get<std::shared_ptr<Ope>>(); - return cap(ope, [name](const char* a_s, size_t a_n, Context& c) { - c.capture_scope_stack.back()[name] = std::string(a_s, a_n); - }); - } - default: { - return sv[0].get<std::shared_ptr<Ope>>(); - } - } - }; + if (ignore) { + return ign(ope); + } else { + return ope; + } + } + case 2: { // (Expression) + return any_cast<std::shared_ptr<Ope>>(sv[0]); + } + case 3: { // TokenBoundary + return tok(any_cast<std::shared_ptr<Ope>>(sv[0])); + } + case 4: { // CaptureScope + return csc(any_cast<std::shared_ptr<Ope>>(sv[0])); + } + case 5: { // Capture + const auto &name = any_cast<std::string>(sv[0]); + auto ope = any_cast<std::shared_ptr<Ope>>(sv[1]); + return cap(ope, [name](const char *a_s, size_t a_n, Context &c) { + auto &cs = c.capture_scope_stack[c.capture_scope_stack_size - 1]; + cs[name] = std::string(a_s, a_n); + }); + } + default: { + return any_cast<std::shared_ptr<Ope>>(sv[0]); + } + } + }; - g["IdentCont"] = [](const SemanticValues& sv) { - return std::string(sv.c_str(), sv.length()); - }; + g["IdentCont"] = [](const SemanticValues &sv) { + return std::string(sv.c_str(), sv.length()); + }; - g["IdentStart"] = [](const SemanticValues& /*sv*/) { - return std::string(); - }; + g["Dictionary"] = [](const SemanticValues &sv) { + auto items = sv.transform<std::string>(); + return dic(items); + }; - g["IdentRest"] = [](const SemanticValues& /*sv*/) { - return std::string(); - }; + g["Literal"] = [](const SemanticValues &sv) { + const auto &tok = sv.tokens.front(); + return lit(resolve_escape_sequence(tok.first, tok.second)); + }; + g["LiteralI"] = [](const SemanticValues &sv) { + const auto &tok = sv.tokens.front(); + return liti(resolve_escape_sequence(tok.first, tok.second)); + }; + g["LiteralD"] = [](const SemanticValues &sv) { + auto &tok = sv.tokens.front(); + return resolve_escape_sequence(tok.first, tok.second); + }; - g["Literal"] = [](const SemanticValues& sv) { - const auto& tok = sv.tokens.front(); - return lit(resolve_escape_sequence(tok.first, tok.second)); - }; - g["Class"] = [](const SemanticValues& sv) { - auto ranges = sv.transform<std::pair<char32_t, char32_t>>(); - return cls(ranges); - }; - g["Range"] = [](const SemanticValues& sv) { - switch (sv.choice()) { - case 0: { - auto s1 = sv[0].get<std::string>(); - auto s2 = sv[1].get<std::string>(); - auto cp1 = decode_codepoint(s1.c_str(), s1.length()); - auto cp2 = decode_codepoint(s2.c_str(), s2.length()); - return std::make_pair(cp1, cp2); - } - case 1: { - auto s = sv[0].get<std::string>(); - auto cp = decode_codepoint(s.c_str(), s.length()); - return std::make_pair(cp, cp); - } - } - return std::make_pair<char32_t, char32_t>(0, 0); - }; - g["Char"] = [](const SemanticValues& sv) { - return resolve_escape_sequence(sv.c_str(), sv.length()); - }; + g["Class"] = [](const SemanticValues &sv) { + auto ranges = sv.transform<std::pair<char32_t, char32_t>>(); + return cls(ranges); + }; + g["NegatedClass"] = [](const SemanticValues &sv) { + auto ranges = sv.transform<std::pair<char32_t, char32_t>>(); + return ncls(ranges); + }; + g["Range"] = [](const SemanticValues &sv) { + switch (sv.choice()) { + case 0: { + auto s1 = any_cast<std::string>(sv[0]); + auto s2 = any_cast<std::string>(sv[1]); + auto cp1 = decode_codepoint(s1.c_str(), s1.length()); + auto cp2 = decode_codepoint(s2.c_str(), s2.length()); + return std::make_pair(cp1, cp2); + } + case 1: { + auto s = any_cast<std::string>(sv[0]); + auto cp = decode_codepoint(s.c_str(), s.length()); + return std::make_pair(cp, cp); + } + } + return std::make_pair<char32_t, char32_t>(0, 0); + }; + g["Char"] = [](const SemanticValues &sv) { + return resolve_escape_sequence(sv.c_str(), sv.length()); + }; - g["AND"] = [](const SemanticValues& sv) { return *sv.c_str(); }; - g["NOT"] = [](const SemanticValues& sv) { return *sv.c_str(); }; - g["QUESTION"] = [](const SemanticValues& sv) { return *sv.c_str(); }; - g["STAR"] = [](const SemanticValues& sv) { return *sv.c_str(); }; - g["PLUS"] = [](const SemanticValues& sv) { return *sv.c_str(); }; + g["AND"] = [](const SemanticValues &sv) { return *sv.c_str(); }; + g["NOT"] = [](const SemanticValues &sv) { return *sv.c_str(); }; + g["QUESTION"] = [](const SemanticValues &sv) { return *sv.c_str(); }; + g["STAR"] = [](const SemanticValues &sv) { return *sv.c_str(); }; + g["PLUS"] = [](const SemanticValues &sv) { return *sv.c_str(); }; - g["DOT"] = [](const SemanticValues& /*sv*/) { return dot(); }; + g["DOT"] = [](const SemanticValues & /*sv*/) { return dot(); }; - g["BeginCap"] = [](const SemanticValues& sv) { return sv.token(); }; + g["BeginCap"] = [](const SemanticValues &sv) { return sv.token(); }; - g["BackRef"] = [&](const SemanticValues& sv) { - return bkr(sv.token()); - }; + g["BackRef"] = [&](const SemanticValues &sv) { return bkr(sv.token()); }; - g["Ignore"] = [](const SemanticValues& sv) { return sv.size() > 0; }; + g["Ignore"] = [](const SemanticValues &sv) { return sv.size() > 0; }; - g["Parameters"] = [](const SemanticValues& sv) { - return sv.transform<std::string>(); - }; + g["Parameters"] = [](const SemanticValues &sv) { + return sv.transform<std::string>(); + }; - g["Arguments"] = [](const SemanticValues& sv) { - return sv.transform<std::shared_ptr<Ope>>(); - }; - } + g["Arguments"] = [](const SemanticValues &sv) { + return sv.transform<std::shared_ptr<Ope>>(); + }; - std::shared_ptr<Grammar> perform_core( - const char* s, - size_t n, - const Rules& rules, - std::string& start, - Log log) - { - Data data; - any dt = &data; - auto r = g["Grammar"].parse(s, n, dt); - - if (!r.ret) { - if (log) { - if (r.message_pos) { - auto line = line_info(s, r.message_pos); - log(line.first, line.second, r.message); - } else { - auto line = line_info(s, r.error_pos); - log(line.first, line.second, "syntax error"); - } - } - return nullptr; + g["PrecedenceClimbing"] = [](const SemanticValues &sv) { + PrecedenceClimbing::BinOpeInfo binOpeInfo; + size_t level = 1; + for (auto v : sv) { + auto tokens = any_cast<std::vector<std::string>>(v); + auto assoc = tokens[0][0]; + for (size_t i = 1; i < tokens.size(); i++) { + const auto &tok = tokens[i]; + binOpeInfo[tok] = std::make_pair(level, assoc); + } + level++; + } + Instruction instruction; + instruction.type = "precedence"; + instruction.data = binOpeInfo; + return instruction; + }; + g["PrecedenceInfo"] = [](const SemanticValues &sv) { + return sv.transform<std::string>(); + }; + g["PrecedenceOpe"] = [](const SemanticValues &sv) { return sv.token(); }; + g["PrecedenceAssoc"] = [](const SemanticValues &sv) { return sv.token(); }; + } + + bool apply_precedence_instruction(Definition &rule, + const PrecedenceClimbing::BinOpeInfo &info, + const char *s, Log log) { + try { + auto &seq = dynamic_cast<Sequence &>(*rule.get_core_operator()); + auto atom = seq.opes_[0]; + auto &rep = dynamic_cast<Repetition &>(*seq.opes_[1]); + auto &seq1 = dynamic_cast<Sequence &>(*rep.ope_); + auto binop = seq1.opes_[0]; + auto atom1 = seq1.opes_[1]; + + auto atom_name = dynamic_cast<Reference &>(*atom).name_; + auto binop_name = dynamic_cast<Reference &>(*binop).name_; + auto atom1_name = dynamic_cast<Reference &>(*atom1).name_; + + if (!rep.is_zom() || atom_name != atom1_name || atom_name == binop_name) { + if (log) { + auto line = line_info(s, rule.s_); + log(line.first, line.second, + "'precedence' instruction cannt be applied to '" + rule.name + + "'."); } + return false; + } - auto& grammar = *data.grammar; + rule.holder_->ope_ = pre(atom, binop, info, rule); + rule.disable_action = true; + } catch (...) { + if (log) { + auto line = line_info(s, rule.s_); + log(line.first, line.second, + "'precedence' instruction cannt be applied to '" + rule.name + + "'."); + } + return false; + } + return true; + } - // User provided rules - for (const auto& x: rules) { - auto name = x.first; - bool ignore = false; - if (!name.empty() && name[0] == '~') { - ignore = true; - name.erase(0, 1); - } - if (!name.empty()) { - auto& rule = grammar[name]; - rule <= x.second; - rule.name = name; - rule.ignoreSemanticValue = ignore; - } + std::shared_ptr<Grammar> perform_core(const char *s, size_t n, + const Rules &rules, std::string &start, + Log log) { + Data data; + any dt = &data; + auto r = g["Grammar"].parse(s, n, dt); + + if (!r.ret) { + if (log) { + if (r.message_pos) { + auto line = line_info(s, r.message_pos); + log(line.first, line.second, r.message); + } else { + auto line = line_info(s, r.error_pos); + log(line.first, line.second, "syntax error"); } + } + return nullptr; + } - // Check duplicated definitions - bool ret = data.duplicates.empty(); + auto &grammar = *data.grammar; - for (const auto& x: data.duplicates) { - if (log) { - const auto& name = x.first; - auto ptr = x.second; - auto line = line_info(s, ptr); - log(line.first, line.second, "'" + name + "' is already defined."); - } - } + // User provided rules + for (const auto &x : rules) { + auto name = x.first; + bool ignore = false; + if (!name.empty() && name[0] == '~') { + ignore = true; + name.erase(0, 1); + } + if (!name.empty()) { + auto &rule = grammar[name]; + rule <= x.second; + rule.name = name; + rule.ignoreSemanticValue = ignore; + } + } - // Check missing definitions - for (auto& x: grammar) { - auto& rule = x.second; + // Check duplicated definitions + bool ret = data.duplicates.empty(); - ReferenceChecker vis(*data.grammar, rule.params); - rule.accept(vis); - for (const auto& y: vis.error_s) { - const auto& name = y.first; - const auto ptr = y.second; - if (log) { - auto line = line_info(s, ptr); - log(line.first, line.second, vis.error_message[name]); - } - ret = false; - } - } + for (const auto &x : data.duplicates) { + if (log) { + const auto &name = x.first; + auto ptr = x.second; + auto line = line_info(s, ptr); + log(line.first, line.second, "'" + name + "' is already defined."); + } + } - if (!ret) { - return nullptr; - } + // Check missing definitions + for (auto &x : grammar) { + auto &rule = x.second; - // Link references - for (auto& x: grammar) { - auto& rule = x.second; - LinkReferences vis(*data.grammar, rule.params); - rule.accept(vis); + ReferenceChecker vis(*data.grammar, rule.params); + rule.accept(vis); + for (const auto &y : vis.error_s) { + const auto &name = y.first; + const auto ptr = y.second; + if (log) { + auto line = line_info(s, ptr); + log(line.first, line.second, vis.error_message[name]); } + ret = false; + } + } + + if (!ret) { return nullptr; } - // Check left recursion - ret = true; + // Link references + for (auto &x : grammar) { + auto &rule = x.second; + LinkReferences vis(*data.grammar, rule.params); + rule.accept(vis); + } - for (auto& x: grammar) { - const auto& name = x.first; - auto& rule = x.second; + // Check left recursion + ret = true; - DetectLeftRecursion vis(name); - rule.accept(vis); - if (vis.error_s) { - if (log) { - auto line = line_info(s, vis.error_s); - log(line.first, line.second, "'" + name + "' is left recursive."); - } - ret = false;; - } - } + for (auto &x : grammar) { + const auto &name = x.first; + auto &rule = x.second; - if (!ret) { - return nullptr; + DetectLeftRecursion vis(name); + rule.accept(vis); + if (vis.error_s) { + if (log) { + auto line = line_info(s, vis.error_s); + log(line.first, line.second, "'" + name + "' is left recursive."); } + ret = false; + } + } - // Set root definition - start = data.start; + if (!ret) { return nullptr; } - // Automatic whitespace skipping - if (grammar.count(WHITESPACE_DEFINITION_NAME)) { - auto& rule = (*data.grammar)[start]; - rule.whitespaceOpe = wsp((*data.grammar)[WHITESPACE_DEFINITION_NAME].get_core_operator()); - } + // Set root definition + auto &start_rule = (*data.grammar)[data.start]; - // Word expression - if (grammar.count(WORD_DEFINITION_NAME)) { - auto& rule = (*data.grammar)[start]; - rule.wordOpe = (*data.grammar)[WORD_DEFINITION_NAME].get_core_operator(); + // Check infinite loop + { + DetectInfiniteLoop vis(data.start_pos, data.start); + start_rule.accept(vis); + if (vis.has_error) { + if (log) { + auto line = line_info(s, vis.error_s); + log(line.first, line.second, + "infinite loop is detected in '" + vis.error_name + "'."); } - - return data.grammar; + return nullptr; + } } - Grammar g; -}; + // Automatic whitespace skipping + if (grammar.count(WHITESPACE_DEFINITION_NAME)) { + for (auto &x : grammar) { + auto &rule = x.second; + auto ope = rule.get_core_operator(); + if (IsLiteralToken::check(*ope)) { rule <= tok(ope); } + } -/*----------------------------------------------------------------------------- - * AST - *---------------------------------------------------------------------------*/ + start_rule.whitespaceOpe = + wsp((*data.grammar)[WHITESPACE_DEFINITION_NAME].get_core_operator()); + } -const int AstDefaultTag = -1; + // Word expression + if (grammar.count(WORD_DEFINITION_NAME)) { + start_rule.wordOpe = + (*data.grammar)[WORD_DEFINITION_NAME].get_core_operator(); + } -#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT -inline constexpr unsigned int str2tag(const char* str, int h = 0) { - return !str[h] ? 5381 : (str2tag(str, h + 1) * 33) ^ static_cast<unsigned char>(str[h]); -} + // Apply instructions + for (const auto &item : data.instructions) { + const auto &name = item.first; + const auto &instruction = item.second; + auto &rule = grammar[name]; -namespace udl { -inline constexpr unsigned int operator "" _(const char* s, size_t) { - return str2tag(s); -} -} -#endif + if (instruction.type == "precedence") { + const auto &info = + any_cast<PrecedenceClimbing::BinOpeInfo>(instruction.data); -template <typename Annotation> -struct AstBase : public Annotation -{ - AstBase(const char* a_path, size_t a_line, size_t a_column, - const char* a_name, size_t a_choice_count, size_t a_choice, - const std::vector<std::shared_ptr<AstBase>>& a_nodes) - : path(a_path ? a_path : "") - , line(a_line) - , column(a_column) - , name(a_name) - , choice_count(a_choice_count) - , choice(a_choice) - , original_name(a_name) - , original_choice_count(a_choice_count) - , original_choice(a_choice) -#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT - , tag(str2tag(a_name)) - , original_tag(tag) -#endif - , is_token(false) - , nodes(a_nodes) - {} + if (!apply_precedence_instruction(rule, info, s, log)) { + return nullptr; + } + } + } - AstBase(const char* a_path, size_t a_line, size_t a_column, - const char* a_name, size_t a_choice_count, size_t a_choice, - const std::string& a_token) - : path(a_path ? a_path : "") - , line(a_line) - , column(a_column) - , name(a_name) - , choice_count(a_choice_count) - , choice(a_choice) - , original_name(a_name) - , original_choice_count(a_choice_count) - , original_choice(a_choice) -#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT - , tag(str2tag(a_name)) - , original_tag(tag) -#endif - , is_token(true) - , token(a_token) - {} + // Set root definition + start = data.start; - AstBase(const AstBase& ast, const char* a_original_name, - size_t a_original_choice_count, size_t a_original_choise) - : path(ast.path) - , line(ast.line) - , column(ast.column) - , name(ast.name) - , choice_count(ast.choice_count) - , choice(ast.choice) - , original_name(a_original_name) - , original_choice_count(a_original_choice_count) - , original_choice(a_original_choise) -#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT - , tag(ast.tag) - , original_tag(str2tag(a_original_name)) -#endif - , is_token(ast.is_token) - , token(ast.token) - , nodes(ast.nodes) - , parent(ast.parent) - {} + return data.grammar; + } - const std::string path; - const size_t line; - const size_t column; - - const std::string name; - const size_t choice_count; - const size_t choice; - const std::string original_name; - const size_t original_choice_count; - const size_t original_choice; -#ifndef PEGLIB_NO_CONSTEXPR_SUPPORT - const unsigned int tag; - const unsigned int original_tag; -#endif + Grammar g; +}; - const bool is_token; - const std::string token; +/*----------------------------------------------------------------------------- + * AST + *---------------------------------------------------------------------------*/ - std::vector<std::shared_ptr<AstBase<Annotation>>> nodes; - std::shared_ptr<AstBase<Annotation>> parent; +template <typename Annotation> struct AstBase : public Annotation { + AstBase(const char *a_path, size_t a_line, size_t a_column, + const char *a_name, size_t a_position, size_t a_length, + size_t a_choice_count, size_t a_choice, + const std::vector<std::shared_ptr<AstBase>> &a_nodes) + : path(a_path ? a_path : ""), line(a_line), column(a_column), + name(a_name), position(a_position), length(a_length), + choice_count(a_choice_count), choice(a_choice), original_name(a_name), + original_choice_count(a_choice_count), original_choice(a_choice), + tag(str2tag(a_name)), original_tag(tag), is_token(false), + nodes(a_nodes) {} + + AstBase(const char *a_path, size_t a_line, size_t a_column, + const char *a_name, size_t a_position, size_t a_length, + size_t a_choice_count, size_t a_choice, const std::string &a_token) + : path(a_path ? a_path : ""), line(a_line), column(a_column), + name(a_name), position(a_position), length(a_length), + choice_count(a_choice_count), choice(a_choice), original_name(a_name), + original_choice_count(a_choice_count), original_choice(a_choice), + tag(str2tag(a_name)), original_tag(tag), is_token(true), + token(a_token) {} + + AstBase(const AstBase &ast, const char *a_original_name, size_t a_position, + size_t a_length, size_t a_original_choice_count, + size_t a_original_choise) + : path(ast.path), line(ast.line), column(ast.column), name(ast.name), + position(a_position), length(a_length), choice_count(ast.choice_count), + choice(ast.choice), original_name(a_original_name), + original_choice_count(a_original_choice_count), + original_choice(a_original_choise), tag(ast.tag), + original_tag(str2tag(a_original_name)), is_token(ast.is_token), + token(ast.token), nodes(ast.nodes), parent(ast.parent) {} + + const std::string path; + const size_t line = 1; + const size_t column = 1; + + const std::string name; + size_t position; + size_t length; + const size_t choice_count; + const size_t choice; + const std::string original_name; + const size_t original_choice_count; + const size_t original_choice; + const unsigned int tag; + const unsigned int original_tag; + + const bool is_token; + const std::string token; + + std::vector<std::shared_ptr<AstBase<Annotation>>> nodes; + std::weak_ptr<AstBase<Annotation>> parent; }; template <typename T> -void ast_to_s_core( - const std::shared_ptr<T>& ptr, - std::string& s, - int level, - std::function<std::string (const T& ast, int level)> fn) { - - const auto& ast = *ptr; - for (auto i = 0; i < level; i++) { - s += " "; - } - auto name = ast.original_name; - if (ast.original_choice_count > 0) { - name += "/" + std::to_string(ast.original_choice); - } - if (ast.name != ast.original_name) { - name += "[" + ast.name + "]"; - } - if (ast.is_token) { - s += "- " + name + " (" + ast.token + ")\n"; - } else { - s += "+ " + name + "\n"; - } - if (fn) { - s += fn(ast, level + 1); - } - for (auto node : ast.nodes) { - ast_to_s_core(node, s, level + 1, fn); - } +void ast_to_s_core(const std::shared_ptr<T> &ptr, std::string &s, int level, + std::function<std::string(const T &ast, int level)> fn) { + const auto &ast = *ptr; + for (auto i = 0; i < level; i++) { + s += " "; + } + auto name = ast.original_name; + if (ast.original_choice_count > 0) { + name += "/" + std::to_string(ast.original_choice); + } + if (ast.name != ast.original_name) { name += "[" + ast.name + "]"; } + if (ast.is_token) { + s += "- " + name + " (" + ast.token + ")\n"; + } else { + s += "+ " + name + "\n"; + } + if (fn) { s += fn(ast, level + 1); } + for (auto node : ast.nodes) { + ast_to_s_core(node, s, level + 1, fn); + } } template <typename T> -std::string ast_to_s( - const std::shared_ptr<T>& ptr, - std::function<std::string (const T& ast, int level)> fn = nullptr) { - - std::string s; - ast_to_s_core(ptr, s, 0, fn); - return s; -} - -struct AstOptimizer -{ - AstOptimizer(bool optimize_nodes, const std::vector<std::string>& filters = {}) - : optimize_nodes_(optimize_nodes) - , filters_(filters) {} - - template <typename T> - std::shared_ptr<T> optimize(std::shared_ptr<T> original, std::shared_ptr<T> parent = nullptr) { - - auto found = std::find(filters_.begin(), filters_.end(), original->name) != filters_.end(); - bool opt = optimize_nodes_ ? !found : found; - - if (opt && original->nodes.size() == 1) { - auto child = optimize(original->nodes[0], parent); - return std::make_shared<T>( - *child, original->name.c_str(), original->choice_count, original->choice); - } - - auto ast = std::make_shared<T>(*original); - ast->parent = parent; - ast->nodes.clear(); - for (auto node : original->nodes) { - auto child = optimize(node, ast); - ast->nodes.push_back(child); - } - return ast; - } +std::string +ast_to_s(const std::shared_ptr<T> &ptr, + std::function<std::string(const T &ast, int level)> fn = nullptr) { + std::string s; + ast_to_s_core(ptr, s, 0, fn); + return s; +} + +struct AstOptimizer { + AstOptimizer(bool optimize_nodes, + const std::vector<std::string> &filters = {}) + : optimize_nodes_(optimize_nodes), filters_(filters) {} + + template <typename T> + std::shared_ptr<T> optimize(std::shared_ptr<T> original, + std::shared_ptr<T> parent = nullptr) { + auto found = std::find(filters_.begin(), filters_.end(), original->name) != + filters_.end(); + bool opt = optimize_nodes_ ? !found : found; + + if (opt && original->nodes.size() == 1) { + auto child = optimize(original->nodes[0], parent); + return std::make_shared<T>(*child, original->name.c_str(), + original->choice_count, original->position, + original->length, original->choice); + } + + auto ast = std::make_shared<T>(*original); + ast->parent = parent; + ast->nodes.clear(); + for (auto node : original->nodes) { + auto child = optimize(node, ast); + ast->nodes.push_back(child); + } + return ast; + } private: - const bool optimize_nodes_; - const std::vector<std::string> filters_; + const bool optimize_nodes_; + const std::vector<std::string> filters_; }; struct EmptyType {}; @@ -3540,228 +4132,197 @@ typedef AstBase<EmptyType> Ast; * parser *---------------------------------------------------------------------------*/ -class parser -{ +class parser { public: - parser() = default; - - parser(const char* s, size_t n, const Rules& rules) { - load_grammar(s, n, rules); - } + parser() = default; - parser(const char* s, const Rules& rules) - : parser(s, strlen(s), rules) {} + parser(const char *s, size_t n, const Rules &rules) { + load_grammar(s, n, rules); + } - parser(const char* s, size_t n) - : parser(s, n, Rules()) {} + parser(const char *s, const Rules &rules) : parser(s, strlen(s), rules) {} - parser(const char* s) - : parser(s, strlen(s), Rules()) {} + parser(const char *s, size_t n) : parser(s, n, Rules()) {} - operator bool() { - return grammar_ != nullptr; - } + parser(const char *s) : parser(s, strlen(s), Rules()) {} - bool load_grammar(const char* s, size_t n, const Rules& rules) { - grammar_ = ParserGenerator::parse(s, n, rules, start_, log); - return grammar_ != nullptr; - } + operator bool() { return grammar_ != nullptr; } - bool load_grammar(const char* s, size_t n) { - return load_grammar(s, n, Rules()); - } + bool load_grammar(const char *s, size_t n, const Rules &rules) { + grammar_ = ParserGenerator::parse(s, n, rules, start_, log); + return grammar_ != nullptr; + } - bool load_grammar(const char* s, const Rules& rules) { - auto n = strlen(s); - return load_grammar(s, n, rules); - } + bool load_grammar(const char *s, size_t n) { + return load_grammar(s, n, Rules()); + } - bool load_grammar(const char* s) { - auto n = strlen(s); - return load_grammar(s, n); - } + bool load_grammar(const char *s, const Rules &rules) { + auto n = strlen(s); + return load_grammar(s, n, rules); + } - bool parse_n(const char* s, size_t n, const char* path = nullptr) const { - if (grammar_ != nullptr) { - const auto& rule = (*grammar_)[start_]; - auto r = rule.parse(s, n, path); - output_log(s, n, r); - return r.ret && r.len == n; - } - return false; - } + bool load_grammar(const char *s) { + auto n = strlen(s); + return load_grammar(s, n); + } - bool parse(const char* s, const char* path = nullptr) const { - auto n = strlen(s); - return parse_n(s, n, path); + bool parse_n(const char *s, size_t n, const char *path = nullptr) const { + if (grammar_ != nullptr) { + const auto &rule = (*grammar_)[start_]; + auto r = rule.parse(s, n, path); + output_log(s, n, r); + return r.ret && r.len == n; } + return false; + } - bool parse_n(const char* s, size_t n, any& dt, const char* path = nullptr) const { - if (grammar_ != nullptr) { - const auto& rule = (*grammar_)[start_]; - auto r = rule.parse(s, n, dt, path); - output_log(s, n, r); - return r.ret && r.len == n; - } - return false; - } + bool parse(const char *s, const char *path = nullptr) const { + auto n = strlen(s); + return parse_n(s, n, path); + } - bool parse(const char* s, any& dt, const char* path = nullptr) const { - auto n = strlen(s); - return parse_n(s, n, dt, path); + bool parse_n(const char *s, size_t n, any &dt, + const char *path = nullptr) const { + if (grammar_ != nullptr) { + const auto &rule = (*grammar_)[start_]; + auto r = rule.parse(s, n, dt, path); + output_log(s, n, r); + return r.ret && r.len == n; } + return false; + } - template <typename T> - bool parse_n(const char* s, size_t n, T& val, const char* path = nullptr) const { - if (grammar_ != nullptr) { - const auto& rule = (*grammar_)[start_]; - auto r = rule.parse_and_get_value(s, n, val, path); - output_log(s, n, r); - return r.ret && r.len == n; - } - return false; - } + bool parse(const char *s, any &dt, const char *path = nullptr) const { + auto n = strlen(s); + return parse_n(s, n, dt, path); + } - template <typename T> - bool parse(const char* s, T& val, const char* path = nullptr) const { - auto n = strlen(s); - return parse_n(s, n, val, path); + template <typename T> + bool parse_n(const char *s, size_t n, T &val, + const char *path = nullptr) const { + if (grammar_ != nullptr) { + const auto &rule = (*grammar_)[start_]; + auto r = rule.parse_and_get_value(s, n, val, path); + output_log(s, n, r); + return r.ret && r.len == n; } + return false; + } - template <typename T> - bool parse_n(const char* s, size_t n, any& dt, T& val, const char* path = nullptr) const { - if (grammar_ != nullptr) { - const auto& rule = (*grammar_)[start_]; - auto r = rule.parse_and_get_value(s, n, dt, val, path); - output_log(s, n, r); - return r.ret && r.len == n; - } - return false; - } + template <typename T> + bool parse(const char *s, T &val, const char *path = nullptr) const { + auto n = strlen(s); + return parse_n(s, n, val, path); + } - template <typename T> - bool parse(const char* s, any& dt, T& val, const char* /*path*/ = nullptr) const { - auto n = strlen(s); - return parse_n(s, n, dt, val); - } - - bool search(const char* s, size_t n, size_t& mpos, size_t& mlen) const { - const auto& rule = (*grammar_)[start_]; - if (grammar_ != nullptr) { - size_t pos = 0; - while (pos < n) { - size_t len = n - pos; - auto r = rule.parse(s + pos, len); - if (r.ret) { - mpos = pos; - mlen = len; - return true; - } - pos++; - } - } - mpos = 0; - mlen = 0; - return false; + template <typename T> + bool parse_n(const char *s, size_t n, any &dt, T &val, + const char *path = nullptr) const { + if (grammar_ != nullptr) { + const auto &rule = (*grammar_)[start_]; + auto r = rule.parse_and_get_value(s, n, dt, val, path); + output_log(s, n, r); + return r.ret && r.len == n; } + return false; + } - bool search(const char* s, size_t& mpos, size_t& mlen) const { - auto n = strlen(s); - return search(s, n, mpos, mlen); - } + template <typename T> + bool parse(const char *s, any &dt, T &val, + const char * /*path*/ = nullptr) const { + auto n = strlen(s); + return parse_n(s, n, dt, val); + } - Definition& operator[](const char* s) { - return (*grammar_)[s]; - } + Definition &operator[](const char *s) { return (*grammar_)[s]; } - const Definition& operator[](const char* s) const { - return (*grammar_)[s]; - } + const Definition &operator[](const char *s) const { return (*grammar_)[s]; } - std::vector<std::string> get_rule_names(){ - std::vector<std::string> rules; - rules.reserve(grammar_->size()); - for (auto const& r : *grammar_) { - rules.emplace_back(r.first); - } - return rules; + std::vector<std::string> get_rule_names() { + std::vector<std::string> rules; + rules.reserve(grammar_->size()); + for (auto const &r : *grammar_) { + rules.emplace_back(r.first); } + return rules; + } - void enable_packrat_parsing() { - if (grammar_ != nullptr) { - auto& rule = (*grammar_)[start_]; - rule.enablePackratParsing = true; - } + void enable_packrat_parsing() { + if (grammar_ != nullptr) { + auto &rule = (*grammar_)[start_]; + rule.enablePackratParsing = true; } + } - template <typename T = Ast> - parser& enable_ast() { - for (auto& x: *grammar_) { - const auto& name = x.first; - auto& rule = x.second; - - if (!rule.action) { - rule.action = [&](const SemanticValues& sv) { - auto line = line_info(sv.ss, sv.c_str()); - - if (rule.is_token()) { - return std::make_shared<T>( - sv.path, line.first, line.second, - name.c_str(), sv.choice_count(), sv.choice(), - sv.token()); - } + template <typename T = Ast> parser &enable_ast() { + for (auto &x : *grammar_) { + const auto &name = x.first; + auto &rule = x.second; - auto ast = std::make_shared<T>( - sv.path, line.first, line.second, - name.c_str(), sv.choice_count(), sv.choice(), - sv.transform<std::shared_ptr<T>>()); + if (!rule.action) { + rule.action = [&](const SemanticValues &sv) { + auto line = sv.line_info(); - for (auto node: ast->nodes) { - node->parent = ast; - } - return ast; - }; - } - } - return *this; + if (rule.is_token()) { + return std::make_shared<T>( + sv.path, line.first, line.second, name.c_str(), + std::distance(sv.ss, sv.c_str()), sv.length(), + sv.choice_count(), sv.choice(), sv.token()); + } + + auto ast = std::make_shared<T>( + sv.path, line.first, line.second, name.c_str(), + std::distance(sv.ss, sv.c_str()), sv.length(), sv.choice_count(), + sv.choice(), sv.transform<std::shared_ptr<T>>()); + + for (auto node : ast->nodes) { + node->parent = ast; + } + return ast; + }; + } } + return *this; + } - void enable_trace(Tracer tracer) { - if (grammar_ != nullptr) { - auto& rule = (*grammar_)[start_]; - rule.tracer = tracer; - } + void enable_trace(TracerEnter tracer_enter, TracerLeave tracer_leave) { + if (grammar_ != nullptr) { + auto &rule = (*grammar_)[start_]; + rule.tracer_enter = tracer_enter; + rule.tracer_leave = tracer_leave; } + } - Log log; + Log log; private: - void output_log(const char* s, size_t n, const Definition::Result& r) const { - if (log) { - if (!r.ret) { - if (r.message_pos) { - auto line = line_info(s, r.message_pos); - log(line.first, line.second, r.message); - } else { - auto line = line_info(s, r.error_pos); - log(line.first, line.second, "syntax error"); - } - } else if (r.len != n) { - auto line = line_info(s, s + r.len); - log(line.first, line.second, "syntax error"); - } + void output_log(const char *s, size_t n, const Definition::Result &r) const { + if (log) { + if (!r.ret) { + if (r.message_pos) { + auto line = line_info(s, r.message_pos); + log(line.first, line.second, r.message); + } else { + auto line = line_info(s, r.error_pos); + log(line.first, line.second, "syntax error"); } + } else if (r.len != n) { + auto line = line_info(s, s + r.len); + log(line.first, line.second, "syntax error"); + } } + } - std::shared_ptr<Grammar> grammar_; - std::string start_; + std::shared_ptr<Grammar> grammar_; + std::string start_; }; } // namespace peg #endif -// vim: et ts=4 sw=4 cin cino={1s ff=unix +// vim: et ts=2 sw=2 cin cino={1s ff=unix #ifndef ARGH #define ARGH //#pragma once @@ -4324,7 +4885,6 @@ class LIB_API TimeStamp { ~TimeStamp() = default; public: - inline TimeStamp& seconds(const int32_t &v) noexcept { m_seconds = v; return *this; @@ -4332,7 +4892,6 @@ class LIB_API TimeStamp { inline int32_t seconds() const noexcept { return m_seconds; } - inline TimeStamp& microseconds(const int32_t &v) noexcept { m_microseconds = v; return *this; @@ -4340,7 +4899,6 @@ class LIB_API TimeStamp { inline int32_t microseconds() const noexcept { return m_microseconds; } - public: template<class Visitor> @@ -4348,28 +4906,22 @@ class LIB_API TimeStamp { (void)fieldId; (void)visitor; // visitor.preVisit(ID(), ShortName(), LongName()); - if (1 == fieldId) { doVisit(1, std::move("int32_t"s), std::move("seconds"s), m_seconds, visitor); return; } - if (2 == fieldId) { doVisit(2, std::move("int32_t"s), std::move("microseconds"s), m_microseconds, visitor); return; } - // visitor.postVisit(); } template<class Visitor> inline void accept(Visitor &visitor) { visitor.preVisit(ID(), ShortName(), LongName()); - doVisit(1, std::move("int32_t"s), std::move("seconds"s), m_seconds, visitor); - doVisit(2, std::move("int32_t"s), std::move("microseconds"s), m_microseconds, visitor); - visitor.postVisit(); } @@ -4377,20 +4929,14 @@ class LIB_API TimeStamp { inline void accept(PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { (void)visit; // Prevent warnings from empty messages. std::forward<PreVisitor>(preVisit)(ID(), ShortName(), LongName()); - doTripletForwardVisit(1, std::move("int32_t"s), std::move("seconds"s), m_seconds, preVisit, visit, postVisit); - doTripletForwardVisit(2, std::move("int32_t"s), std::move("microseconds"s), m_microseconds, preVisit, visit, postVisit); - std::forward<PostVisitor>(postVisit)(); } private: - int32_t m_seconds{ 0 }; // field identifier = 1. - int32_t m_microseconds{ 0 }; // field identifier = 2. - }; }} @@ -4531,7 +5077,6 @@ class LIB_API Envelope { ~Envelope() = default; public: - inline Envelope& dataType(const int32_t &v) noexcept { m_dataType = v; return *this; @@ -4539,7 +5084,6 @@ class LIB_API Envelope { inline int32_t dataType() const noexcept { return m_dataType; } - inline Envelope& serializedData(const std::string &v) noexcept { m_serializedData = v; return *this; @@ -4547,7 +5091,6 @@ class LIB_API Envelope { inline std::string serializedData() const noexcept { return m_serializedData; } - inline Envelope& sent(const cluon::data::TimeStamp &v) noexcept { m_sent = v; return *this; @@ -4555,7 +5098,6 @@ class LIB_API Envelope { inline cluon::data::TimeStamp sent() const noexcept { return m_sent; } - inline Envelope& received(const cluon::data::TimeStamp &v) noexcept { m_received = v; return *this; @@ -4563,7 +5105,6 @@ class LIB_API Envelope { inline cluon::data::TimeStamp received() const noexcept { return m_received; } - inline Envelope& sampleTimeStamp(const cluon::data::TimeStamp &v) noexcept { m_sampleTimeStamp = v; return *this; @@ -4571,7 +5112,6 @@ class LIB_API Envelope { inline cluon::data::TimeStamp sampleTimeStamp() const noexcept { return m_sampleTimeStamp; } - inline Envelope& senderStamp(const uint32_t &v) noexcept { m_senderStamp = v; return *this; @@ -4579,7 +5119,6 @@ class LIB_API Envelope { inline uint32_t senderStamp() const noexcept { return m_senderStamp; } - public: template<class Visitor> @@ -4587,56 +5126,42 @@ class LIB_API Envelope { (void)fieldId; (void)visitor; // visitor.preVisit(ID(), ShortName(), LongName()); - if (1 == fieldId) { doVisit(1, std::move("int32_t"s), std::move("dataType"s), m_dataType, visitor); return; } - if (2 == fieldId) { doVisit(2, std::move("std::string"s), std::move("serializedData"s), m_serializedData, visitor); return; } - if (3 == fieldId) { doVisit(3, std::move("cluon::data::TimeStamp"s), std::move("sent"s), m_sent, visitor); return; } - if (4 == fieldId) { doVisit(4, std::move("cluon::data::TimeStamp"s), std::move("received"s), m_received, visitor); return; } - if (5 == fieldId) { doVisit(5, std::move("cluon::data::TimeStamp"s), std::move("sampleTimeStamp"s), m_sampleTimeStamp, visitor); return; } - if (6 == fieldId) { doVisit(6, std::move("uint32_t"s), std::move("senderStamp"s), m_senderStamp, visitor); return; } - // visitor.postVisit(); } template<class Visitor> inline void accept(Visitor &visitor) { visitor.preVisit(ID(), ShortName(), LongName()); - doVisit(1, std::move("int32_t"s), std::move("dataType"s), m_dataType, visitor); - doVisit(2, std::move("std::string"s), std::move("serializedData"s), m_serializedData, visitor); - doVisit(3, std::move("cluon::data::TimeStamp"s), std::move("sent"s), m_sent, visitor); - doVisit(4, std::move("cluon::data::TimeStamp"s), std::move("received"s), m_received, visitor); - doVisit(5, std::move("cluon::data::TimeStamp"s), std::move("sampleTimeStamp"s), m_sampleTimeStamp, visitor); - doVisit(6, std::move("uint32_t"s), std::move("senderStamp"s), m_senderStamp, visitor); - visitor.postVisit(); } @@ -4644,36 +5169,22 @@ class LIB_API Envelope { inline void accept(PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { (void)visit; // Prevent warnings from empty messages. std::forward<PreVisitor>(preVisit)(ID(), ShortName(), LongName()); - doTripletForwardVisit(1, std::move("int32_t"s), std::move("dataType"s), m_dataType, preVisit, visit, postVisit); - doTripletForwardVisit(2, std::move("std::string"s), std::move("serializedData"s), m_serializedData, preVisit, visit, postVisit); - doTripletForwardVisit(3, std::move("cluon::data::TimeStamp"s), std::move("sent"s), m_sent, preVisit, visit, postVisit); - doTripletForwardVisit(4, std::move("cluon::data::TimeStamp"s), std::move("received"s), m_received, preVisit, visit, postVisit); - doTripletForwardVisit(5, std::move("cluon::data::TimeStamp"s), std::move("sampleTimeStamp"s), m_sampleTimeStamp, preVisit, visit, postVisit); - doTripletForwardVisit(6, std::move("uint32_t"s), std::move("senderStamp"s), m_senderStamp, preVisit, visit, postVisit); - std::forward<PostVisitor>(postVisit)(); } private: - int32_t m_dataType{ 0 }; // field identifier = 1. - std::string m_serializedData{ ""s }; // field identifier = 2. - cluon::data::TimeStamp m_sent{ }; // field identifier = 3. - cluon::data::TimeStamp m_received{ }; // field identifier = 4. - cluon::data::TimeStamp m_sampleTimeStamp{ }; // field identifier = 5. - uint32_t m_senderStamp{ 0 }; // field identifier = 6. - }; }} @@ -4814,7 +5325,6 @@ class LIB_API PlayerCommand { ~PlayerCommand() = default; public: - inline PlayerCommand& command(const uint8_t &v) noexcept { m_command = v; return *this; @@ -4822,7 +5332,6 @@ class LIB_API PlayerCommand { inline uint8_t command() const noexcept { return m_command; } - inline PlayerCommand& seekTo(const float &v) noexcept { m_seekTo = v; return *this; @@ -4830,7 +5339,6 @@ class LIB_API PlayerCommand { inline float seekTo() const noexcept { return m_seekTo; } - public: template<class Visitor> @@ -4838,28 +5346,22 @@ class LIB_API PlayerCommand { (void)fieldId; (void)visitor; // visitor.preVisit(ID(), ShortName(), LongName()); - if (1 == fieldId) { doVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, visitor); return; } - if (2 == fieldId) { doVisit(2, std::move("float"s), std::move("seekTo"s), m_seekTo, visitor); return; } - // visitor.postVisit(); } template<class Visitor> inline void accept(Visitor &visitor) { visitor.preVisit(ID(), ShortName(), LongName()); - doVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, visitor); - doVisit(2, std::move("float"s), std::move("seekTo"s), m_seekTo, visitor); - visitor.postVisit(); } @@ -4867,20 +5369,14 @@ class LIB_API PlayerCommand { inline void accept(PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { (void)visit; // Prevent warnings from empty messages. std::forward<PreVisitor>(preVisit)(ID(), ShortName(), LongName()); - doTripletForwardVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, preVisit, visit, postVisit); - doTripletForwardVisit(2, std::move("float"s), std::move("seekTo"s), m_seekTo, preVisit, visit, postVisit); - std::forward<PostVisitor>(postVisit)(); } private: - uint8_t m_command{ 0 }; // field identifier = 1. - float m_seekTo{ 0.0f }; // field identifier = 2. - }; }} @@ -5021,7 +5517,6 @@ class LIB_API PlayerStatus { ~PlayerStatus() = default; public: - inline PlayerStatus& state(const uint8_t &v) noexcept { m_state = v; return *this; @@ -5029,7 +5524,6 @@ class LIB_API PlayerStatus { inline uint8_t state() const noexcept { return m_state; } - inline PlayerStatus& numberOfEntries(const uint32_t &v) noexcept { m_numberOfEntries = v; return *this; @@ -5037,7 +5531,6 @@ class LIB_API PlayerStatus { inline uint32_t numberOfEntries() const noexcept { return m_numberOfEntries; } - inline PlayerStatus& currentEntryForPlayback(const uint32_t &v) noexcept { m_currentEntryForPlayback = v; return *this; @@ -5045,7 +5538,6 @@ class LIB_API PlayerStatus { inline uint32_t currentEntryForPlayback() const noexcept { return m_currentEntryForPlayback; } - public: template<class Visitor> @@ -5053,35 +5545,27 @@ class LIB_API PlayerStatus { (void)fieldId; (void)visitor; // visitor.preVisit(ID(), ShortName(), LongName()); - if (1 == fieldId) { doVisit(1, std::move("uint8_t"s), std::move("state"s), m_state, visitor); return; } - if (2 == fieldId) { doVisit(2, std::move("uint32_t"s), std::move("numberOfEntries"s), m_numberOfEntries, visitor); return; } - if (3 == fieldId) { doVisit(3, std::move("uint32_t"s), std::move("currentEntryForPlayback"s), m_currentEntryForPlayback, visitor); return; } - // visitor.postVisit(); } template<class Visitor> inline void accept(Visitor &visitor) { visitor.preVisit(ID(), ShortName(), LongName()); - doVisit(1, std::move("uint8_t"s), std::move("state"s), m_state, visitor); - doVisit(2, std::move("uint32_t"s), std::move("numberOfEntries"s), m_numberOfEntries, visitor); - doVisit(3, std::move("uint32_t"s), std::move("currentEntryForPlayback"s), m_currentEntryForPlayback, visitor); - visitor.postVisit(); } @@ -5089,24 +5573,16 @@ class LIB_API PlayerStatus { inline void accept(PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { (void)visit; // Prevent warnings from empty messages. std::forward<PreVisitor>(preVisit)(ID(), ShortName(), LongName()); - doTripletForwardVisit(1, std::move("uint8_t"s), std::move("state"s), m_state, preVisit, visit, postVisit); - doTripletForwardVisit(2, std::move("uint32_t"s), std::move("numberOfEntries"s), m_numberOfEntries, preVisit, visit, postVisit); - doTripletForwardVisit(3, std::move("uint32_t"s), std::move("currentEntryForPlayback"s), m_currentEntryForPlayback, preVisit, visit, postVisit); - std::forward<PostVisitor>(postVisit)(); } private: - uint8_t m_state{ 0 }; // field identifier = 1. - uint32_t m_numberOfEntries{ 0 }; // field identifier = 2. - uint32_t m_currentEntryForPlayback{ 0 }; // field identifier = 3. - }; }} @@ -5120,10 +5596,188 @@ struct isTripletForwardVisitable<cluon::data::PlayerStatus> { }; #endif + +/* + * THIS IS AN AUTO-GENERATED FILE. DO NOT MODIFY AS CHANGES MIGHT BE OVERWRITTEN! + */ + +#ifndef VISITABLE_TYPE_TRAIT +#define VISITABLE_TYPE_TRAIT +#include <cstdint> +#include <string> +#include <utility> + +template<bool b> +struct visitorSelector { + template<typename T, class Visitor> + static void impl(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, Visitor &visitor) { + visitor.visit(fieldIdentifier, std::move(typeName), std::move(name), value); + } +}; + +template<> +struct visitorSelector<true> { + template<typename T, class Visitor> + static void impl(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, Visitor &visitor) { + visitor.visit(fieldIdentifier, std::move(typeName), std::move(name), value); + } +}; + +template<typename T> +struct isVisitable { + static const bool value = false; +}; + +template<typename T, class Visitor> +void doVisit(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, Visitor &visitor) { + visitorSelector<isVisitable<T>::value >::impl(fieldIdentifier, std::move(typeName), std::move(name), value, visitor); +} +#endif + +#ifndef TRIPLET_FORWARD_VISITABLE_TYPE_TRAIT +#define TRIPLET_FORWARD_VISITABLE_TYPE_TRAIT +#include <cstdint> +#include <string> +#include <utility> + +template<bool b> +struct tripletForwardVisitorSelector { + template<typename T, class PreVisitor, class Visitor, class PostVisitor> + static void impl(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { + (void)preVisit; + (void)postVisit; + std::forward<Visitor>(visit)(fieldIdentifier, std::move(typeName), std::move(name), value); + } +}; + +template<> +struct tripletForwardVisitorSelector<true> { + template<typename T, class PreVisitor, class Visitor, class PostVisitor> + static void impl(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { + (void)fieldIdentifier; + (void)typeName; + (void)name; + // Apply preVisit, visit, and postVisit on value. + value.accept(preVisit, visit, postVisit); + } +}; + +template<typename T> +struct isTripletForwardVisitable { + static const bool value = false; +}; + +template< typename T, class PreVisitor, class Visitor, class PostVisitor> +void doTripletForwardVisit(uint32_t fieldIdentifier, std::string &&typeName, std::string &&name, T &value, PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { + tripletForwardVisitorSelector<isTripletForwardVisitable<T>::value >::impl(fieldIdentifier, std::move(typeName), std::move(name), value, std::move(preVisit), std::move(visit), std::move(postVisit)); // NOLINT +} +#endif + + +#ifndef CLUON_DATA_RECORDERCOMMAND_HPP +#define CLUON_DATA_RECORDERCOMMAND_HPP + +#ifdef WIN32 + // Export symbols if compile flags "LIB_SHARED" and "LIB_EXPORTS" are set on Windows. + #ifdef LIB_SHARED + #ifdef LIB_EXPORTS + #define LIB_API __declspec(dllexport) + #else + #define LIB_API __declspec(dllimport) + #endif + #else + // Disable definition if linking statically. + #define LIB_API + #endif +#else + // Disable definition for non-Win32 systems. + #define LIB_API +#endif + +#include <string> +#include <utility> +namespace cluon { namespace data { +using namespace std::string_literals; // NOLINT +class LIB_API RecorderCommand { + private: + static constexpr const char* TheShortName = "RecorderCommand"; + static constexpr const char* TheLongName = "cluon.data.RecorderCommand"; + + public: + inline static int32_t ID() { + return 11; + } + inline static const std::string ShortName() { + return TheShortName; + } + inline static const std::string LongName() { + return TheLongName; + } + + public: + RecorderCommand() = default; + RecorderCommand(const RecorderCommand&) = default; + RecorderCommand& operator=(const RecorderCommand&) = default; + RecorderCommand(RecorderCommand&&) = default; + RecorderCommand& operator=(RecorderCommand&&) = default; + ~RecorderCommand() = default; + + public: + inline RecorderCommand& command(const uint8_t &v) noexcept { + m_command = v; + return *this; + } + inline uint8_t command() const noexcept { + return m_command; + } + + public: + template<class Visitor> + inline void accept(uint32_t fieldId, Visitor &visitor) { + (void)fieldId; + (void)visitor; +// visitor.preVisit(ID(), ShortName(), LongName()); + if (1 == fieldId) { + doVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, visitor); + return; + } +// visitor.postVisit(); + } + + template<class Visitor> + inline void accept(Visitor &visitor) { + visitor.preVisit(ID(), ShortName(), LongName()); + doVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, visitor); + visitor.postVisit(); + } + + template<class PreVisitor, class Visitor, class PostVisitor> + inline void accept(PreVisitor &&preVisit, Visitor &&visit, PostVisitor &&postVisit) { + (void)visit; // Prevent warnings from empty messages. + std::forward<PreVisitor>(preVisit)(ID(), ShortName(), LongName()); + doTripletForwardVisit(1, std::move("uint8_t"s), std::move("command"s), m_command, preVisit, visit, postVisit); + std::forward<PostVisitor>(postVisit)(); + } + + private: + uint8_t m_command{ 0 }; // field identifier = 1. +}; +}} + +template<> +struct isVisitable<cluon::data::RecorderCommand> { + static const bool value = true; +}; +template<> +struct isTripletForwardVisitable<cluon::data::RecorderCommand> { + static const bool value = true; +}; +#endif + /* * MIT License * - * Copyright (c) 2018 Christian Berger + * Copyright (c) 2018-2020 Christian Berger * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -5191,7 +5845,7 @@ inline std::string replaceAll(const std::string &str, * @return std::vector<std:string> where the given string is split along delimiter. */ inline std::vector<std::string> split(const std::string &str, - const char &delimiter) noexcept { + const char &delimiter) noexcept { std::vector<std::string> retVal{}; std::string::size_type prev{0}; for (std::string::size_type i{str.find_first_of(delimiter, prev)}; @@ -5200,10 +5854,16 @@ inline std::vector<std::string> split(const std::string &str, if (i != prev) { retVal.emplace_back(str.substr(prev, i - prev)); } + else { + retVal.emplace_back(""); + } } - if ((prev > 0) && (prev < str.size())) { + if (prev < str.size()) { retVal.emplace_back(str.substr(prev, str.size() - prev)); } + else if (prev > 0) { + retVal.emplace_back(""); + } return retVal; } @@ -5249,7 +5909,7 @@ inline int64_t toMicroseconds(const cluon::data::TimeStamp &tp) noexcept { /** * @param AFTER First time stamp. * @param BEFORE Second time stamp. - * @return Delta (BEFORE - AFTER) between two TimeStamps in microseconds. + * @return Delta (AFTER - BEFORE) between two TimeStamps in microseconds. */ inline int64_t deltaInMicroseconds(const cluon::data::TimeStamp &AFTER, const cluon::data::TimeStamp &BEFORE) noexcept { return toMicroseconds(AFTER) - toMicroseconds(BEFORE); @@ -5277,7 +5937,7 @@ inline cluon::data::TimeStamp convert(const std::chrono::system_clock::time_poin } /** - * @return TimeStamp of now. + * @return TimeStamp of now from std::chrono::system_clock. */ inline cluon::data::TimeStamp now() noexcept { return convert(std::chrono::system_clock::now()); @@ -5322,6 +5982,14 @@ inline cluon::data::TimeStamp now() noexcept { #include <sys/endian.h> #elif (defined(_WIN16) || defined(_WIN32) || defined(_WIN64)) #if BYTE_ORDER == LITTLE_ENDIAN + // Add missing definitions for MinGW. + #ifndef htonll + #define htonll(x) ((1==htonl(1)) ? (x) : (((uint64_t)htonl((x) & 0xFFFFFFFFUL)) << 32) | htonl((uint32_t)((x) >> 32))) + #endif + #ifndef ntohll + #define ntohll(x) ((1==ntohl(1)) ? (x) : (((uint64_t)ntohl((x) & 0xFFFFFFFFUL)) << 32) | ntohl((uint32_t)((x) >> 32))) + #endif + #define htobe16(x) htons(x) #define htole16(x) (x) #define be16toh(x) ntohs(x) @@ -5696,7 +6364,7 @@ message myMessage.SubName [id = 1] { cluon::MessageParser mp; auto retVal = mp.parse(std::string(spec)); -if (retVal.second == cluon::MessageParser::MessageParserErrorCodes::NO_ERROR) { +if (retVal.second == cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR) { auto listOfMessages = retVal.first; for (auto message : listOfMessages) { message.accept([](const cluon::MetaMessage &mm){ std::cout << "Message name = " << mm.messageName() << @@ -5707,7 +6375,7 @@ std::endl; }); */ class LIBCLUON_API MessageParser { public: - enum MessageParserErrorCodes : uint8_t { NO_ERROR = 0, SYNTAX_ERROR = 1, DUPLICATE_IDENTIFIERS = 2 }; + enum MessageParserErrorCodes : uint8_t { NO_MESSAGEPARSER_ERROR = 0, SYNTAX_ERROR = 1, DUPLICATE_IDENTIFIERS = 2 }; private: MessageParser(const MessageParser &) = delete; @@ -5723,7 +6391,7 @@ class LIBCLUON_API MessageParser { * * @param input Message specification. * @return Pair: List of cluon::MetaMessages describing the specified messages and error code: - * NO_ERROR: The given specification could be parsed successfully (list moght be non-empty). + * NO_MESSAGEPARSER_ERROR: The given specification could be parsed successfully (list moght be non-empty). * SYNTAX_ERROR: The given specification could not be parsed successfully (list is empty). * DUPLICATE_IDENTIFIERS: The given specification contains ambiguous names or identifiers (list is empty). */ @@ -5890,13 +6558,36 @@ class LIBCLUON_API NotifyingPipeline { private: std::function<void(T &&)> m_delegate; - std::atomic<bool> m_pipelineThreadRunning{false}; - std::thread m_pipelineThread{}; - std::mutex m_pipelineMutex{}; - std::condition_variable m_pipelineCondition{}; + std::atomic<bool> m_pipelineThreadRunning{false}; + std::thread m_pipelineThread{}; + std::mutex m_pipelineMutex{}; + std::condition_variable m_pipelineCondition{}; + + std::deque<T> m_pipeline{}; +}; +} // namespace cluon + +#endif +/* + * Copyright (C) 2019 Christian Berger + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#ifndef CLUON_IPV4TOOLS_HPP +#define CLUON_IPV4TOOLS_HPP + +#include <string> + +namespace cluon { + +/** + * @return IPv4-formatted string for the given hostname or the empty string. + */ +std::string getIPv4FromHostname(const std::string &hostname) noexcept; - std::deque<T> m_pipeline{}; -}; } // namespace cluon #endif @@ -6303,6 +6994,7 @@ class LIBCLUON_API TCPConnection { private: mutable std::mutex m_socketMutex{}; int32_t m_socket{-1}; + bool m_cleanup{true};//if not created from TCPServer,call WSACleanup struct sockaddr_in m_address {}; std::atomic<bool> m_readFromSocketThreadRunning{false}; @@ -7374,14 +8066,16 @@ class LIBCLUON_API ToCSVVisitor { void visit(uint32_t &id, std::string &&typeName, std::string &&name, T &value) noexcept { (void)id; (void)typeName; - constexpr bool IS_NESTED{true}; - ToCSVVisitor csvVisitor(name, m_delimiter, m_withHeader, IS_NESTED); - value.accept(csvVisitor); + if ((0 == m_mask.count(id)) || m_mask[id]) { + constexpr bool IS_NESTED{true}; + ToCSVVisitor csvVisitor(name, m_delimiter, m_withHeader, IS_NESTED); + value.accept(csvVisitor); - if (m_fillHeader) { - m_bufferHeader << csvVisitor.m_bufferHeader.str(); + if (m_fillHeader) { + m_bufferHeader << csvVisitor.m_bufferHeader.str(); + } + m_bufferValues << csvVisitor.m_bufferValues.str(); } - m_bufferValues << csvVisitor.m_bufferValues.str(); } private: @@ -7525,7 +8219,7 @@ std::cout << generatedMessageSpecification << std::endl; cluon::MessageParser mp; auto retVal = mp.parse(generatedMessageSpecification); -std::cout << (cluon::MessageParser::MessageParserErrorCodes::NO_ERROR == retVal.second); +std::cout << (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == retVal.second); \endcode */ class LIBCLUON_API ToODVDVisitor { @@ -7970,7 +8664,7 @@ message MyMessage [id = 123] { cluon::MessageParser mp; auto retVal = mp.parse(std::string(messageSpecification)); -if (cluon::MessageParser::MessageParserErrorCodes::NO_ERROR == retVal.second) { +if (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == retVal.second) { cluon::GenericMessage gm; auto listOfMetaMessages = retVal.first; gm.createFrom(listOfMetaMessages[0], listOfMetaMessages); @@ -8065,7 +8759,7 @@ message MyMessage [id = 123] { cluon::MessageParser mp; auto retVal = mp.parse(std::string(messageSpecification)); -if (cluon::MessageParser::MessageParserErrorCodes::NO_ERROR == retVal.second) { +if (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == retVal.second) { cluon::GenericMessage gm; auto listOfMetaMessages = retVal.first; gm.createFrom(listOfMetaMessages[0], listOfMetaMessages); @@ -9247,19 +9941,14 @@ namespace cluon { inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCodes> MessageParser::parse(const std::string &input) { const char *grammarMessageSpecificationLanguage = R"( MESSAGES_SPECIFICATION <- PACKAGE_DECLARATION? MESSAGE_DECLARATION* - PACKAGE_DECLARATION <- 'package' PACKAGE_IDENTIFIER ';' - PACKAGE_IDENTIFIER <- < IDENTIFIER ('.' IDENTIFIER)* > - - MESSAGE_DECLARATION <- 'message' MESSAGE_IDENTIFIER MESSAGE_OPTIONS '{' FIELD* '}' - MESSAGE_IDENTIFIER <- < IDENTIFIER ('.' IDENTIFIER)* > - MESSAGE_OPTIONS <- '[' 'id' '=' NATURAL_NUMBER ','? ']' + PACKAGE_DECLARATION <- 'package' PACKAGE_NAME ';' + PACKAGE_NAME <- < NAME ('.' NAME)* > - FIELD <- PRIMITIVE_FIELD + MESSAGE_DECLARATION <- 'message' MESSAGE_NAME '[' IDENTIFIER ','? ']' '{' FIELD* '}' + MESSAGE_NAME <- < NAME ('.' NAME)* > - PRIMITIVE_FIELD <- PRIMITIVE_TYPE IDENTIFIER ('[' PRIMITIVE_FIELD_OPTIONS ']')? ';' - PRIMITIVE_FIELD_OPTIONS <- PRIMITIVE_FIELD_DEFAULT? ','? NUMERICAL_FIELD_IDENTIFIER? - NUMERICAL_FIELD_IDENTIFIER <- 'id' '=' NATURAL_NUMBER - PRIMITIVE_FIELD_DEFAULT <- 'default' '=' (FLOAT_NUMBER / BOOL / CHARACTER / STRING) + FIELD <- PRIMITIVE_TYPE NAME ('[' (((DEFAULT / IDENTIFIER) ','?)+)? ']')? ';' + DEFAULT <- 'default' '=' (FLOAT_NUMBER / BOOL / CHARACTER / STRING) PRIMITIVE_TYPE <- < 'bool' / 'float' / 'double' / 'char' / 'bytes' / 'string' / @@ -9269,9 +9958,11 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode 'int64' / 'uint64' / MESSAGE_TYPE > - MESSAGE_TYPE <- < IDENTIFIER ('.' IDENTIFIER)* > + MESSAGE_TYPE <- < NAME ('.' NAME)* > - IDENTIFIER <- < [a-zA-Z][a-zA-Z0-9_]* > + IDENTIFIER <- 'id' '=' NATURAL_NUMBER + + NAME <- < [a-zA-Z][a-zA-Z0-9_]* > DIGIT <- < [0-9] > NATURAL_NUMBER <- < [1-9] DIGIT* > FLOAT_NUMBER <- < ('+' / '-')? DIGIT DIGIT* (('.') DIGIT*)? > @@ -9294,6 +9985,7 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode std::vector<int32_t> &numericalFieldIdentifiers) { bool retVal = true; // First, we need to visit the children of AST node MESSAGES_SPECIFICATION. + if ("MESSAGES_SPECIFICATION" == ast.name) { for (const auto &node : ast.nodes) { retVal &= checkForUniqueFieldNames(*node, prefix, messageNames, fieldNames, numericalMessageIdentifiers, numericalFieldIdentifiers); @@ -9339,12 +10031,12 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode retVal = true; for (const auto &node : ast.nodes) { - if ("MESSAGE_IDENTIFIER" == node->name) { + if ("MESSAGE_NAME" == node->original_name) { prefix = node->token; messageNames.push_back(::stringtoolbox::trim(prefix)); - } else if ("NATURAL_NUMBER" == node->name) { + } else if ("IDENTIFIER" == node->original_name) { numericalMessageIdentifiers.push_back(std::stoi(node->token)); - } else if ("PRIMITIVE_FIELD" == node->name) { + } else if ("FIELD" == node->original_name) { retVal &= checkForUniqueFieldNames(*node, prefix, messageNames, fieldNames, numericalMessageIdentifiers, numericalFieldIdentifiers); } } @@ -9384,26 +10076,18 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode } } } - // Within AST node MESSAGE_DECLARATION, we have PRIMITIVE_FIELD from + // Within AST node MESSAGE_DECLARATION, we have FIELD from // which we need to extract the field "token". - if (ast.name == "PRIMITIVE_FIELD") { - // Extract the value of entry "IDENTIFIER". - auto nodeIdentifier = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->name == "IDENTIFIER"); }); - if (nodeIdentifier != std::end(ast.nodes)) { - fieldNames.push_back((*nodeIdentifier)->token); + if (ast.original_name == "FIELD") { + // Extract the value of entry "NAME". + auto nodeName = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->original_name == "NAME"); }); + if (nodeName != std::end(ast.nodes)) { + fieldNames.push_back((*nodeName)->token); } - // Visit this node's children to check for duplicated numerical identifiers. - for (const auto &node : ast.nodes) { - retVal &= checkForUniqueFieldNames(*node, prefix, messageNames, fieldNames, numericalMessageIdentifiers, numericalFieldIdentifiers); - } - } - // Within AST node PRIMITIVE_FIELD, we have PRIMITIVE_FIELD_OPTIONS from - // which we need to extract the field "token". - if (ast.name == "PRIMITIVE_FIELD_OPTIONS") { // Extract the value of entry "IDENTIFIER". auto nodeNumericalFieldIdentifier - = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->name == "NATURAL_NUMBER"); }); + = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->original_name == "IDENTIFIER"); }); if (nodeNumericalFieldIdentifier != std::end(ast.nodes)) { numericalFieldIdentifiers.push_back(std::stoi((*nodeNumericalFieldIdentifier)->token)); } @@ -9423,49 +10107,39 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode mm.packageName(::stringtoolbox::trim(_packageName)); uint32_t fieldIdentifierCounter{0}; for (const auto &e : _node.nodes) { - if ("MESSAGE_IDENTIFIER" == e->name) { + if ("MESSAGE_NAME" == e->original_name) { std::string _messageName = e->token; mm.messageName(::stringtoolbox::trim(_messageName)); - } else if ("NATURAL_NUMBER" == e->name) { + } else if ("IDENTIFIER" == e->original_name) { mm.messageIdentifier(std::stoi(e->token)); - } else if ("PRIMITIVE_FIELD" == e->name) { - std::string _fieldName; - auto fieldName = std::find_if(std::begin(e->nodes), std::end(e->nodes), [](auto a) { return (a->name == "IDENTIFIER"); }); - if (fieldName != std::end(e->nodes)) { - _fieldName = (*fieldName)->token; - } - + } else if ("FIELD" == e->original_name) { std::string _fieldDataType; - auto fieldDataType = std::find_if(std::begin(e->nodes), std::end(e->nodes), [](auto a) { return (a->name == "PRIMITIVE_TYPE"); }); - if (fieldDataType != std::end(e->nodes)) { - _fieldDataType = (*fieldDataType)->token; - } - - fieldIdentifierCounter++; // Automatically count expected field identifiers in case of missing - // field options. - std::string _fieldIdentifier; - auto fieldIdentifier = std::find_if(std::begin(e->nodes), std::end(e->nodes), [](auto a) { return (a->name == "NATURAL_NUMBER"); }); - if (fieldIdentifier != std::end(e->nodes)) { - _fieldIdentifier = (*fieldIdentifier)->token; - } - + std::string _fieldName; std::string _fieldDefaultInitializerValue; - auto primitiveFieldOptions - = std::find_if(std::begin(e->nodes), std::end(e->nodes), [](auto a) { return (a->name == "PRIMITIVE_FIELD_OPTIONS"); }); - if (primitiveFieldOptions != std::end(e->nodes)) { - for (const auto &f : (*primitiveFieldOptions)->nodes) { - if ("NATURAL_NUMBER" != f->name) { - if ("STRING" == f->name) { - _fieldDefaultInitializerValue = "\"" + f->token + "\""; // NOLINT - } else if ("CHARACTER" == f->name) { - _fieldDefaultInitializerValue = "'" + f->token + "'"; - } else { - _fieldDefaultInitializerValue = f->token; - } + std::string _fieldIdentifier; + for (const auto &f : e->nodes) { + if ("PRIMITIVE_TYPE" == f->original_name) { + _fieldDataType = f->token; + } else if ("NAME" == f->original_name) { + _fieldName = f->token; + } else if ("DEFAULT" == f->original_name) { + if ("STRING" == f->name) { + _fieldDefaultInitializerValue = "\"" + f->token + "\""; // NOLINT + } else if ("CHARACTER" == f->name) { + _fieldDefaultInitializerValue = "'" + f->token + "'"; + } else { + _fieldDefaultInitializerValue = f->token; } + } else if ("IDENTIFIER" == f->original_name) { + _fieldIdentifier = f->token; } } + if (_fieldIdentifier.empty()) { + // Automatically count expected field identifiers in case of missing field options. + fieldIdentifierCounter++; + } + std::map<std::string, MetaMessage::MetaField::MetaFieldDataTypes> STRING_TO_DATATYPE_MAP = { {"bool", MetaMessage::MetaField::BOOL_T}, {"char", MetaMessage::MetaField::CHAR_T}, @@ -9504,8 +10178,8 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode // Case: "package XYZ" present. if ("MESSAGES_SPECIFICATION" == ast.name) { - // Extract the value of entry "PACKAGE_IDENTIFIER". - auto nodeIdentifier = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->name == "PACKAGE_IDENTIFIER"); }); + // Extract the value of entry "PACKAGE_NAME". + auto nodeIdentifier = std::find_if(std::begin(ast.nodes), std::end(ast.nodes), [](auto a) { return (a->name == "PACKAGE_NAME"); }); std::string packageName; if (nodeIdentifier != std::end(ast.nodes)) { packageName = (*nodeIdentifier)->token; @@ -9552,7 +10226,7 @@ inline std::pair<std::vector<MetaMessage>, MessageParser::MessageParserErrorCode std::vector<int32_t> tmpNumericalFieldIdentifiers{}; if (check4UniqueFieldNames(*ast, tmpPrefix, tmpMessageNames, tmpFieldNames, tmpNumericalMessageIdentifiers, tmpNumericalFieldIdentifiers)) { transform2MetaMessages(*ast, listOfMetaMessages); - retVal = {listOfMetaMessages, MessageParserErrorCodes::NO_ERROR}; + retVal = {listOfMetaMessages, MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR}; } else { retVal = {listOfMetaMessages, MessageParserErrorCodes::DUPLICATE_IDENTIFIERS}; } @@ -9616,21 +10290,103 @@ inline TerminateHandler::TerminateHandler() noexcept { } // namespace cluon /* - * Copyright (C) 2017-2018 Christian Berger + * Copyright (C) 2019 Christian Berger * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -//#include "cluon/UDPSender.hpp" -//#include "cluon/UDPPacketSizeConstraints.hpp" +//#include "cluon/IPv4Tools.hpp" // clang-format off #ifdef WIN32 + #include <Winsock2.h> // for WSAStartUp + #include <ws2tcpip.h> #include <iostream> #else #include <arpa/inet.h> + #include <netdb.h> + + #if defined(BSD) + #include <netinet/in.h> + #include <sys/socket.h> + #endif +#endif +// clang-format on + +#include <cstring> + +namespace cluon { + +inline std::string getIPv4FromHostname(const std::string &hostname) noexcept { +#ifdef WIN32 + // Load Winsock 2.2 DLL. + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + std::cerr << "[cluon::getIPv4FromHostname] Error while calling WSAStartUp: " << WSAGetLastError() << std::endl; + } +#endif + std::string result{""}; + if (!hostname.empty()) { + struct addrinfo hint; + { + std::memset(&hint, 1, sizeof(struct addrinfo)); + hint.ai_flags = AI_CANONNAME; + hint.ai_family = AF_INET; + hint.ai_socktype = 0; + hint.ai_protocol = 0; + hint.ai_addrlen = 0; + hint.ai_canonname = nullptr; + hint.ai_addr = nullptr; + hint.ai_next = nullptr; + } + + struct addrinfo *listOfHosts{nullptr}; + if (0 == getaddrinfo(hostname.c_str(), nullptr, &hint, &listOfHosts)) { + for(struct addrinfo *e = listOfHosts; nullptr != listOfHosts; listOfHosts = listOfHosts->ai_next) { + if (nullptr != e) { + if (AF_INET == e->ai_family) { + struct sockaddr_in *sinp = reinterpret_cast<struct sockaddr_in*>(e->ai_addr); + char buf[INET_ADDRSTRLEN]; + const char *addr = inet_ntop(AF_INET, &sinp->sin_addr, buf, INET_ADDRSTRLEN); + if ( (nullptr != addr) && (result.empty()) ) { + result = std::string(addr); + break; + } + } + } + } + } + + if (nullptr != listOfHosts) { + freeaddrinfo(listOfHosts); + } + } +#ifdef WIN32 + WSACleanup(); +#endif + return result; +} + +} // namespace cluon +/* + * Copyright (C) 2017-2018 Christian Berger + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +//#include "cluon/UDPSender.hpp" +//#include "cluon/IPv4Tools.hpp" +//#include "cluon/UDPPacketSizeConstraints.hpp" + +// clang-format off +#ifndef WIN32 + #include <arpa/inet.h> + #include <ifaddrs.h> + #include <netdb.h> #include <sys/socket.h> #include <sys/types.h> #include <unistd.h> @@ -9640,6 +10396,7 @@ inline TerminateHandler::TerminateHandler() noexcept { #include <cerrno> #include <cstring> #include <algorithm> +#include <iostream> #include <iterator> #include <sstream> #include <vector> @@ -9650,7 +10407,7 @@ inline UDPSender::UDPSender(const std::string &sendToAddress, uint16_t sendToPor : m_socketMutex() , m_sendToAddress() { // Decompose given address into tokens to check validity with numerical IPv4 address. - std::string tmp{sendToAddress}; + std::string tmp{cluon::getIPv4FromHostname(sendToAddress)}; std::replace(tmp.begin(), tmp.end(), '.', ' '); std::istringstream sstr{tmp}; std::vector<int> sendToAddressTokens{std::istream_iterator<int>(sstr), std::istream_iterator<int>()}; @@ -9673,6 +10430,64 @@ inline UDPSender::UDPSender(const std::string &sendToAddress, uint16_t sendToPor m_socket = ::socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); +#ifndef WIN32 + // Check whether given address is a broadcast address. + bool isBroadcast{false}; + { + isBroadcast |= (sendToAddress == "255.255.255.255"); + if (!isBroadcast) { + struct ifaddrs *ifaddr{nullptr}; + if (0 == getifaddrs(&ifaddr)) { + for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (NULL == ifa->ifa_addr) continue; // LCOV_EXCL_LINE + char broadcastAddress[NI_MAXHOST]; +#ifdef __APPLE__ + if (NULL == ifa->ifa_dstaddr) continue; // LCOV_EXCL_LINE + if (0 == ::getnameinfo(ifa->ifa_dstaddr, + sizeof(struct sockaddr_in), + broadcastAddress, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST)) +#else + if (NULL == ifa->ifa_ifu.ifu_broadaddr) continue; // LCOV_EXCL_LINE + if (0 == ::getnameinfo(ifa->ifa_ifu.ifu_broadaddr, + sizeof(struct sockaddr_in), + broadcastAddress, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST)) +#endif + { + std::string _tmp{broadcastAddress}; + isBroadcast |= (_tmp.compare(sendToAddress) == 0); + } + } + freeifaddrs(ifaddr); + } + } + } +#endif + +#ifndef WIN32 + if (!(m_socket < 0) && isBroadcast) { + // Enabling broadcast. + uint32_t YES = 1; + // clang-format off + auto retVal = ::setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char *>(&YES), sizeof(YES)); // NOLINT + // clang-format on + if (0 > retVal) { +#ifdef WIN32 // LCOV_EXCL_LINE + auto errorCode = WSAGetLastError(); +#else + auto errorCode = errno; // LCOV_EXCL_LINE +#endif // LCOV_EXCL_LINE + std::cerr << "[cluon::UDPSender] Failed to perform socket operation: "; // LCOV_EXCL_LINE +#ifdef WIN32 // LCOV_EXCL_LINE + std::cerr << errorCode << std::endl; +#else + std::cerr << ::strerror(errorCode) << " (" << errorCode << ")" << std::endl; // LCOV_EXCL_LINE +#endif // LCOV_EXCL_LINE + } + } +#endif + // Bind to random address/port but store sender port. if (!(m_socket < 0)) { struct sockaddr_in sendFromAddress; @@ -9753,6 +10568,7 @@ inline std::pair<ssize_t, int32_t> UDPSender::send(std::string &&data) const noe */ //#include "cluon/UDPReceiver.hpp" +//#include "cluon/IPv4Tools.hpp" //#include "cluon/TerminateHandler.hpp" //#include "cluon/UDPPacketSizeConstraints.hpp" @@ -9767,11 +10583,15 @@ inline std::pair<ssize_t, int32_t> UDPSender::send(std::string &&data) const noe #include <iostream> #else + #ifdef __linux__ + #include <linux/sockios.h> + #endif + #include <arpa/inet.h> + #include <fcntl.h> #include <sys/ioctl.h> #include <sys/socket.h> #include <sys/types.h> - #include <fcntl.h> #include <unistd.h> #endif @@ -9802,7 +10622,7 @@ inline UDPReceiver::UDPReceiver(const std::string &receiveFromAddress, , m_readFromSocketThread() , m_delegate(std::move(delegate)) { // Decompose given address string to check validity with numerical IPv4 address. - std::string tmp{receiveFromAddress}; + std::string tmp{cluon::getIPv4FromHostname(receiveFromAddress)}; std::replace(tmp.begin(), tmp.end(), '.', ' '); std::istringstream sstr{tmp}; std::vector<int> receiveFromAddressTokens{std::istream_iterator<int>(sstr), std::istream_iterator<int>()}; @@ -9813,7 +10633,7 @@ inline UDPReceiver::UDPReceiver(const std::string &receiveFromAddress, && (0 < receiveFromPort)) { // Check for valid IP address. struct sockaddr_in tmpSocketAddress {}; - const bool isValid = (0 < ::inet_pton(AF_INET, receiveFromAddress.c_str(), &(tmpSocketAddress.sin_addr))) && (224 > receiveFromAddressTokens[0]); + const bool isValid = (0 < ::inet_pton(AF_INET, receiveFromAddress.c_str(), &(tmpSocketAddress.sin_addr))) && (224 > receiveFromAddressTokens[0] || 255 == receiveFromAddressTokens[0]); // Accept regular IP addresses (ie., non-multicast addesses and the network-wide broadcast address 255.255.255.255. // Check for UDP multicast, i.e., IP address range [225.0.0.1 - 239.255.255.255]. m_isMulticast = (((224 < receiveFromAddressTokens[0]) && (receiveFromAddressTokens[0] <= 239)) @@ -9821,6 +10641,44 @@ inline UDPReceiver::UDPReceiver(const std::string &receiveFromAddress, && ((0 <= receiveFromAddressTokens[2]) && (receiveFromAddressTokens[2] <= 255)) && ((1 <= receiveFromAddressTokens[3]) && (receiveFromAddressTokens[3] <= 255))); +#ifndef WIN32 + // Check whether given address is a broadcast address. + bool isBroadcast{false}; + { + isBroadcast |= (receiveFromAddress == "255.255.255.255"); + if (!isBroadcast) { + struct ifaddrs *ifaddr{nullptr}; + if (0 == getifaddrs(&ifaddr)) { + for (struct ifaddrs *ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (NULL == ifa->ifa_addr) continue; // LCOV_EXCL_LINE + + if (ifa->ifa_addr->sa_family == AF_INET) { + char broadcastAddress[NI_MAXHOST]; +#ifdef __APPLE__ + if (NULL == ifa->ifa_dstaddr) continue; // LCOV_EXCL_LINE + if (0 == ::getnameinfo(ifa->ifa_dstaddr, + sizeof(struct sockaddr_in), + broadcastAddress, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST)) +#else + if (NULL == ifa->ifa_ifu.ifu_broadaddr) continue; // LCOV_EXCL_LINE + if (0 == ::getnameinfo(ifa->ifa_ifu.ifu_broadaddr, + sizeof(struct sockaddr_in), + broadcastAddress, NI_MAXHOST, + NULL, 0, NI_NUMERICHOST)) +#endif + { + std::string _tmp{broadcastAddress}; + isBroadcast |= (_tmp.compare(receiveFromAddress) == 0); + } + } + } + freeifaddrs(ifaddr); + } + } + } +#endif + std::memset(&m_receiveFromAddress, 0, sizeof(m_receiveFromAddress)); #ifdef WIN32 // According to http://www.sockets.com/err_lst1.htm, the binding is @@ -9866,6 +10724,24 @@ inline UDPReceiver::UDPReceiver(const std::string &receiveFromAddress, } } +#ifndef WIN32 + if (!(m_socket < 0) && isBroadcast) { + // Enabling broadcast. + uint32_t YES = 1; + // clang-format off + auto retVal = ::setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char *>(&YES), sizeof(YES)); // NOLINT + // clang-format on + if (0 > retVal) { +#ifdef WIN32 // LCOV_EXCL_LINE + auto errorCode = WSAGetLastError(); +#else + auto errorCode = errno; // LCOV_EXCL_LINE +#endif // LCOV_EXCL_LINE + closeSocket(errorCode); // LCOV_EXCL_LINE + } + } +#endif + if (!(m_socket < 0)) { // Trying to enable non_blocking mode. #ifdef WIN32 // LCOV_EXCL_LINE @@ -10162,6 +11038,7 @@ inline void UDPReceiver::readFromSocket() noexcept { */ //#include "cluon/TCPConnection.hpp" +//#include "cluon/IPv4Tools.hpp" //#include "cluon/TerminateHandler.hpp" // clang-format off @@ -10169,6 +11046,10 @@ inline void UDPReceiver::readFromSocket() noexcept { #include <errno.h> #include <iostream> #else + #ifdef __linux__ + #include <linux/sockios.h> + #endif + #include <arpa/inet.h> #include <sys/ioctl.h> #include <sys/socket.h> @@ -10190,6 +11071,7 @@ namespace cluon { inline TCPConnection::TCPConnection(const int32_t &socket) noexcept : m_socket(socket) + , m_cleanup(false) , m_newDataDelegate(nullptr) , m_connectionLostDelegate(nullptr) { if (!(m_socket < 0)) { @@ -10204,7 +11086,8 @@ inline TCPConnection::TCPConnection(const std::string &address, : m_newDataDelegate(std::move(newDataDelegate)) , m_connectionLostDelegate(std::move(connectionLostDelegate)) { // Decompose given address string to check validity with numerical IPv4 address. - std::string tmp{address}; + std::string resolvedHostname{cluon::getIPv4FromHostname(address)}; + std::string tmp{resolvedHostname}; std::replace(tmp.begin(), tmp.end(), '.', ' '); std::istringstream sstr{tmp}; std::vector<int> addressTokens{std::istream_iterator<int>(sstr), std::istream_iterator<int>()}; @@ -10213,10 +11096,10 @@ inline TCPConnection::TCPConnection(const std::string &address, && !(std::end(addressTokens) != std::find_if(addressTokens.begin(), addressTokens.end(), [](int a) { return (a < 0) || (a > 255); })) && (0 < port)) { // Check for valid IP address. struct sockaddr_in tmpSocketAddress {}; - const bool isValid = (0 < ::inet_pton(AF_INET, address.c_str(), &(tmpSocketAddress.sin_addr))); + const bool isValid = (0 < ::inet_pton(AF_INET, resolvedHostname.c_str(), &(tmpSocketAddress.sin_addr))); if (isValid) { std::memset(&m_address, 0, sizeof(m_address)); - m_address.sin_addr.s_addr = ::inet_addr(address.c_str()); + m_address.sin_addr.s_addr = ::inet_addr(resolvedHostname.c_str()); m_address.sin_family = AF_INET; m_address.sin_port = htons(port); #ifdef WIN32 @@ -10284,7 +11167,9 @@ inline void TCPConnection::closeSocket(int errorCode) noexcept { #ifdef WIN32 ::shutdown(m_socket, SD_BOTH); ::closesocket(m_socket); - WSACleanup(); + if(m_cleanup){ + WSACleanup(); + } #else ::shutdown(m_socket, SHUT_RDWR); // Disallow further read/write operations. ::close(m_socket); @@ -11849,7 +12734,7 @@ inline void FromMsgPackVisitor::visit(uint32_t id, std::string &&typeName, std:: } // namespace cluon /* - * Copyright (C) 2017-2018 Christian Berger + * Copyright (C) 2017-2019 Christian Berger * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -11890,9 +12775,9 @@ inline std::map<std::string, FromJSONVisitor::JSONKeyValue> FromJSONVisitor::rea if (m.size() > 0) { std::string match{m[0]}; std::vector<std::string> retVal = stringtoolbox::split(match, ':'); - if ((retVal.size() == 1) || ((retVal.size() == 2) && (stringtoolbox::trim(retVal[1]).size() == 0))) { + if ( (retVal.size() == 2) && (stringtoolbox::trim(retVal[1]).size() == 0) ) { std::string keyOfNestedObject{stringtoolbox::trim(retVal[0])}; - keyOfNestedObject = stringtoolbox::split(keyOfNestedObject, '"')[0]; + keyOfNestedObject = stringtoolbox::split(keyOfNestedObject, '"')[1]; { std::string suffix(m.suffix()); suffix = stringtoolbox::trim(suffix); @@ -11909,11 +12794,11 @@ inline std::map<std::string, FromJSONVisitor::JSONKeyValue> FromJSONVisitor::rea result[keyOfNestedObject] = kv; } - if ((retVal.size() == 2) && (stringtoolbox::trim(retVal[1]).size() > 0)) { + if ( (retVal.size() == 2) && (stringtoolbox::trim(retVal[1]).size() > 0) ) { auto e = std::make_pair(stringtoolbox::trim(retVal[0]), stringtoolbox::trim(retVal[1])); JSONKeyValue kv; - kv.m_key = stringtoolbox::split(e.first, '"')[0]; + kv.m_key = stringtoolbox::split(e.first, '"')[1]; if ((e.second.size() > 0) && (e.second.at(0) == '"')) { kv.m_type = JSONConstants::STRING; @@ -13168,7 +14053,7 @@ inline int32_t LCMToGenericMessage::setMessageSpecification(const std::string &m cluon::MessageParser mp; auto parsingResult = mp.parse(ms); - if (cluon::MessageParser::MessageParserErrorCodes::NO_ERROR == parsingResult.second) { + if (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == parsingResult.second) { m_listOfMetaMessages = parsingResult.first; for (const auto &mm : m_listOfMetaMessages) { m_scopeOfMetaMessages[mm.messageName()] = mm; } retVal = static_cast<int32_t>(m_listOfMetaMessages.size()); @@ -13801,7 +14686,7 @@ inline int32_t EnvelopeConverter::setMessageSpecification(const std::string &ms) cluon::MessageParser mp; auto parsingResult = mp.parse(ms); - if (cluon::MessageParser::MessageParserErrorCodes::NO_ERROR == parsingResult.second) { + if (cluon::MessageParser::MessageParserErrorCodes::NO_MESSAGEPARSER_ERROR == parsingResult.second) { m_listOfMetaMessages = parsingResult.first; for (const auto &mm : m_listOfMetaMessages) { m_scopeOfMetaMessages[mm.messageIdentifier()] = mm; } retVal = static_cast<int32_t>(m_listOfMetaMessages.size()); @@ -14041,7 +14926,7 @@ inline void Player::initializeIndex() noexcept { const int32_t percentage = static_cast<int32_t>((static_cast<float>(m_recFile.tellg()) * 100.0f) / static_cast<float>(fileLength)); if ((percentage % 5 == 0) && (percentage != oldPercentage)) { - std::clog << "[cluon::Player]: Indexed " << percentage << "% from " << m_file << "." << std::endl; + std::cerr << "[cluon::Player]: Indexed " << percentage << "% from " << m_file << "." << std::endl; oldPercentage = percentage; } } @@ -14049,11 +14934,11 @@ inline void Player::initializeIndex() noexcept { } const cluon::data::TimeStamp AFTER{cluon::time::now()}; - std::clog << "[cluon::Player]: " << m_file << " contains " << m_index.size() << " entries; " + std::cerr << "[cluon::Player]: " << m_file << " contains " << m_index.size() << " entries; " << "read " << totalBytesRead << " bytes " << "in " << cluon::time::deltaInMicroseconds(AFTER, BEFORE) / static_cast<int64_t>(1000 * 1000) << "s." << std::endl; } else { - std::clog << "[cluon::Player]: " << m_file << " could not be opened." << std::endl; + std::cerr << "[cluon::Player]: " << m_file << " could not be opened." << std::endl; } } @@ -14078,19 +14963,19 @@ inline void Player::resetIterators() noexcept { inline void Player::computeInitialCacheLevelAndFillCache() noexcept { if (m_recFileValid && (m_index.size() > 0)) { - int64_t smallestSampleTimePoint = std::numeric_limits<int64_t>::max(); - int64_t largestSampleTimePoint = std::numeric_limits<int64_t>::min(); + int64_t smallestSampleTimePoint = (std::numeric_limits<int64_t>::max)(); + int64_t largestSampleTimePoint = (std::numeric_limits<int64_t>::min)(); for (auto it = m_index.begin(); it != m_index.end(); it++) { - smallestSampleTimePoint = std::min(smallestSampleTimePoint, it->first); - largestSampleTimePoint = std::max(largestSampleTimePoint, it->first); + smallestSampleTimePoint = (std::min)(smallestSampleTimePoint, it->first); + largestSampleTimePoint = (std::max)(largestSampleTimePoint, it->first); } const uint32_t ENTRIES_TO_READ_PER_SECOND_FOR_REALTIME_REPLAY = static_cast<uint32_t>(std::ceil(static_cast<float>(m_index.size()) * (static_cast<float>(Player::ONE_SECOND_IN_MICROSECONDS)) / static_cast<float>(largestSampleTimePoint - smallestSampleTimePoint))); - m_desiredInitialLevel = std::max<uint32_t>(ENTRIES_TO_READ_PER_SECOND_FOR_REALTIME_REPLAY * Player::LOOK_AHEAD_IN_S, MIN_ENTRIES_FOR_LOOK_AHEAD); + m_desiredInitialLevel = (std::max<uint32_t>)(ENTRIES_TO_READ_PER_SECOND_FOR_REALTIME_REPLAY * Player::LOOK_AHEAD_IN_S, MIN_ENTRIES_FOR_LOOK_AHEAD); - std::clog << "[cluon::Player]: Initializing cache with " << m_desiredInitialLevel << " entries." << std::endl; + std::cerr << "[cluon::Player]: Initializing cache with " << m_desiredInitialLevel << " entries." << std::endl; resetCaches(); resetIterators(); @@ -14248,7 +15133,7 @@ inline void Player::seekTo(float ratio) noexcept { // Fast forward. m_numberOfReturnedEnvelopesInTotal = 0; - std::clog << "[cluon::Player]: Seeking to " << static_cast<float>(numberOfEntriesInIndex) * ratio << "/" << numberOfEntriesInIndex << std::endl; + std::cerr << "[cluon::Player]: Seeking to " << static_cast<float>(numberOfEntriesInIndex) * ratio << "/" << numberOfEntriesInIndex << std::endl; if (0 < ratio) { for (m_numberOfReturnedEnvelopesInTotal = 0; m_numberOfReturnedEnvelopesInTotal < static_cast<uint32_t>(static_cast<float>(numberOfEntriesInIndex) * ratio) - 1; @@ -14256,7 +15141,10 @@ inline void Player::seekTo(float ratio) noexcept { m_currentEnvelopeToReplay++; } } - m_nextEntryToReadFromRecFile = m_previousEnvelopeAlreadyReplayed = m_currentEnvelopeToReplay; + try { + std::lock_guard<std::mutex> lck(m_indexMutex); + m_nextEntryToReadFromRecFile = m_previousEnvelopeAlreadyReplayed = m_currentEnvelopeToReplay; + } catch (...) {} // LCOV_EXCL_LINE // Refill cache. m_envelopeCache.clear(); @@ -14266,7 +15154,7 @@ inline void Player::seekTo(float ratio) noexcept { if ((0 < ratio) && (ratio < 1)) { getNextEnvelopeToBeReplayed(); } - std::clog << "[cluon::Player]: Seeking done." << std::endl; + std::cerr << "[cluon::Player]: Seeking done." << std::endl; if (enableThreading) { m_threading = enableThreading; @@ -14352,7 +15240,7 @@ inline float Player::checkRefillingCache(const uint32_t &numberOfEntries, float if (numberOfEntries < 0.35 * m_desiredInitialLevel) { const uint32_t entriesReadFromFile = fillEnvelopeCache(static_cast<uint32_t>(refillMultiplicator * static_cast<float>(m_desiredInitialLevel))); if (entriesReadFromFile > 0) { - std::clog << "[cluon::Player]: Number of entries in cache: " << numberOfEntries << ". " << entriesReadFromFile << " added to cache. " + std::cerr << "[cluon::Player]: Number of entries in cache: " << numberOfEntries << ". " << entriesReadFromFile << " added to cache. " << m_envelopeCache.size() << " entries available." << std::endl; refillMultiplicator *= 1.25f; } @@ -14579,14 +15467,16 @@ inline std::pair<bool, cluon::data::TimeStamp> SharedMemory::getTimeStamp() noex #ifndef WIN32 if ((retVal = isLocked())) { struct stat fileStatus; - fstat(m_fdForTimeStamping, &fileStatus); + auto r = fstat(m_fdForTimeStamping, &fileStatus); + if (0 == r) { #ifdef __APPLE__ - sampleTimeStamp.seconds(static_cast<int32_t>(fileStatus.st_mtimespec.tv_sec)) - .microseconds(static_cast<int32_t>(fileStatus.st_mtimespec.tv_nsec/1000)); + sampleTimeStamp.seconds(static_cast<int32_t>(fileStatus.st_mtimespec.tv_sec)) + .microseconds(static_cast<int32_t>(fileStatus.st_mtimespec.tv_nsec/1000)); #else - sampleTimeStamp.seconds(static_cast<int32_t>(fileStatus.st_mtim.tv_sec)) - .microseconds(static_cast<int32_t>(fileStatus.st_mtim.tv_nsec/1000)); + sampleTimeStamp.seconds(static_cast<int32_t>(fileStatus.st_mtim.tv_sec)) + .microseconds(static_cast<int32_t>(fileStatus.st_mtim.tv_nsec/1000)); #endif + } } #endif @@ -15428,7 +16318,8 @@ inline bool SharedMemory::validSysV() noexcept { /* * Boost Software License - Version 1.0 * - * Copyright 2015-2018 Kevin Wojniak + * Mustache v4.1 + * Copyright 2015-2020 Kevin Wojniak * * Permission is hereby granted, free of charge, to any person or organization * obtaining a copy of the software and accompanying documentation covered by @@ -15457,6 +16348,7 @@ inline bool SharedMemory::validSysV() noexcept { #define KAINJOW_MUSTACHE_HPP #include <cassert> +#include <cctype> #include <functional> #include <iostream> #include <memory> @@ -15470,11 +16362,11 @@ namespace mustache { template <typename string_type> string_type trim(const string_type& s) { auto it = s.begin(); - while (it != s.end() && isspace(*it)) { + while (it != s.end() && std::isspace(*it)) { it++; } auto rit = s.rbegin(); - while (rit.base() != it && isspace(*rit)) { + while (rit.base() != it && std::isspace(*rit)) { rit++; } return {it, rit.base()}; @@ -15850,6 +16742,7 @@ const string_type delimiter_set<string_type>::default_end(2, '}'); template <typename string_type> class basic_context { public: + virtual ~basic_context() = default; virtual void push(const basic_data<string_type>* data) = 0; virtual void pop() = 0; @@ -15874,7 +16767,7 @@ public: virtual void pop() override { items_.erase(items_.begin()); } - + virtual const basic_data<string_type>* get(const string_type& name) const override { // process {{.}} name if (name.size() == 1 && name.at(0) == '.') { @@ -15924,12 +16817,35 @@ private: std::vector<const basic_data<string_type>*> items_; }; +template <typename string_type> +class line_buffer_state { +public: + string_type data; + bool contained_section_tag = false; + + bool is_empty_or_contains_only_whitespace() const { + for (const auto ch : data) { + // don't look at newlines + if (ch != ' ' && ch != '\t') { + return false; + } + } + return true; + } + + void clear() { + data.clear(); + contained_section_tag = false; + } +}; + template <typename string_type> class context_internal { public: basic_context<string_type>& ctx; delimiter_set<string_type> delim_set; - + line_buffer_state<string_type> line_buffer; + context_internal(basic_context<string_type>& a_ctx) : ctx(a_ctx) { @@ -15997,19 +16913,19 @@ public: skip, }; using walk_callback = std::function<walk_control(component&)>; - + component() {} component(const string_type& t, string_size_type p) : text(t), position(p) {} - + bool is_text() const { return tag.type == tag_type::text; } - + bool is_newline() const { return is_text() && ((text.size() == 2 && text[0] == '\r' && text[1] == '\n') || (text.size() == 1 && (text[0] == '\n' || text[0] == '\r'))); } - + bool is_non_newline_whitespace() const { return is_text() && !is_newline() && text.size() == 1 && (text[0] == ' ' || text[0] == '\t'); } @@ -16021,7 +16937,7 @@ public: } } } - + private: walk_control walk(const walk_callback& callback) { walk_control control{callback(*this)}; @@ -16032,7 +16948,9 @@ private: } for (auto& child : children) { control = child.walk(callback); - assert(control == walk_control::walk); + if (control == walk_control::stop) { + return control; + } } return control; } @@ -16050,19 +16968,19 @@ private: void parse(const string_type& input, context_internal<string_type>& ctx, component<string_type>& root_component, string_type& error_message) const { using string_size_type = typename string_type::size_type; using streamstring = std::basic_ostringstream<typename string_type::value_type>; - + const string_type brace_delimiter_end_unescaped(3, '}'); const string_size_type input_size{input.size()}; bool current_delimiter_is_brace{ctx.delim_set.is_default()}; - + std::vector<component<string_type>*> sections{&root_component}; std::vector<string_size_type> section_starts; string_type current_text; string_size_type current_text_position = -1; - + current_text.reserve(input_size); - + const auto process_current_text = [¤t_text, ¤t_text_position, §ions]() { if (!current_text.empty()) { const component<string_type> comp{current_text, current_text_position}; @@ -16071,7 +16989,7 @@ private: current_text_position = -1; } }; - + const std::vector<string_type> whitespace{ string_type(1, '\r') + string_type(1, '\n'), string_type(1, '\n'), @@ -16079,10 +16997,10 @@ private: string_type(1, ' '), string_type(1, '\t'), }; - + for (string_size_type input_position = 0; input_position != input_size;) { bool parse_tag = false; - + if (input.compare(input_position, ctx.delim_set.begin.size(), ctx.delim_set.begin) == 0) { process_current_text(); @@ -16097,12 +17015,12 @@ private: const component<string_type> comp{whitespace_text, input_position}; sections.back()->children.push_back(comp); input_position += whitespace_text.size(); - + parsed_whitespace = true; break; } } - + if (!parsed_whitespace) { if (current_text.empty()) { current_text_position = input_position; @@ -16111,14 +17029,14 @@ private: input_position++; } } - + if (!parse_tag) { continue; } - + // Find the next tag start delimiter const string_size_type tag_location_start = input_position; - + // Find the next tag end delimiter string_size_type tag_contents_location{tag_location_start + ctx.delim_set.begin.size()}; const bool tag_is_unescaped_var{current_delimiter_is_brace && tag_location_start != (input_size - 2) && input.at(tag_contents_location) == ctx.delim_set.begin.at(0)}; @@ -16134,7 +17052,7 @@ private: error_message.assign(ss.str()); return; } - + // Parse tag const string_type tag_contents{trim(string_type{input, tag_contents_location, tag_location_end - tag_contents_location})}; component<string_type> comp; @@ -16154,10 +17072,10 @@ private: } comp.position = tag_location_start; sections.back()->children.push_back(comp); - + // Start next search after this tag input_position = tag_location_end + current_tag_delimiter_end_size; - + // Push or pop sections if (comp.tag.is_section_begin()) { sections.push_back(§ions.back()->children.back()); @@ -16174,9 +17092,9 @@ private: section_starts.pop_back(); } } - + process_current_text(); - + // Check for sections without an ending tag root_component.walk_children([&error_message](component<string_type>& comp) -> typename component<string_type>::walk_control { if (!comp.tag.is_section_begin()) { @@ -16195,17 +17113,17 @@ private: return; } } - + bool is_set_delimiter_valid(const string_type& delimiter) const { // "Custom delimiters may not contain whitespace or the equals sign." for (const auto ch : delimiter) { - if (ch == '=' || isspace(ch)) { + if (ch == '=' || std::isspace(ch)) { return false; } } return true; } - + bool parse_set_delimiter_tag(const string_type& contents, delimiter_set<string_type>& delimiter_set) const { // Smallest legal tag is "=X X=" if (contents.size() < 5) { @@ -16230,7 +17148,7 @@ private: delimiter_set.end = end; return true; } - + void parse_tag_contents(bool is_unescaped_var, const string_type& contents, mstch_tag<string_type>& tag) const { if (is_unescaped_var) { tag.type = tag_type::unescaped_variable; @@ -16288,7 +17206,7 @@ public: bool is_valid() const { return error_message_.empty(); } - + const string_type& error_message() const { return error_message_; } @@ -16305,7 +17223,7 @@ public: }); return stream; } - + string_type render(const basic_data<string_type>& data) { std::basic_ostringstream<typename string_type::value_type> ss; return render(data, ss).str(); @@ -16342,15 +17260,11 @@ private: : escape_(html_escape<string_type>) { } - + basic_mustache(const string_type& input, context_internal<string_type>& ctx) : basic_mustache() { parser<string_type> parser{input, ctx, root_component_, error_message_}; } - - void walk(const typename component<string_type>::walk_callback& callback) { - root_component_.walk_children(callback); - } string_type render(context_internal<string_type>& ctx) { std::basic_ostringstream<typename string_type::value_type> ss; @@ -16360,18 +17274,47 @@ private: return ss.str(); } - void render(const render_handler& handler, context_internal<string_type>& ctx) { - walk([&handler, &ctx, this](component<string_type>& comp) -> typename component<string_type>::walk_control { + void render(const render_handler& handler, context_internal<string_type>& ctx, bool root_renderer = true) { + root_component_.walk_children([&handler, &ctx, this](component<string_type>& comp) -> typename component<string_type>::walk_control { return render_component(handler, ctx, comp); }); + // process the last line, but only for the top-level renderer + if (root_renderer) { + render_current_line(handler, ctx, nullptr); + } + } + + void render_current_line(const render_handler& handler, context_internal<string_type>& ctx, const component<string_type>* comp) const { + // We're at the end of a line, so check the line buffer state to see + // if the line had tags in it, and also if the line is now empty or + // contains whitespace only. if this situation is true, skip the line. + bool output = true; + if (ctx.line_buffer.contained_section_tag && ctx.line_buffer.is_empty_or_contains_only_whitespace()) { + output = false; + } + if (output) { + handler(ctx.line_buffer.data); + if (comp) { + handler(comp->text); + } + } + ctx.line_buffer.clear(); + } + + void render_result(context_internal<string_type>& ctx, const string_type& text) const { + ctx.line_buffer.data.append(text); } typename component<string_type>::walk_control render_component(const render_handler& handler, context_internal<string_type>& ctx, component<string_type>& comp) { if (comp.is_text()) { - handler(comp.text); + if (comp.is_newline()) { + render_current_line(handler, ctx, &comp); + } else { + render_result(ctx, comp.text); + } return component<string_type>::walk_control::walk; } - + const mstch_tag<string_type>& tag{comp.tag}; const basic_data<string_type>* var = nullptr; switch (tag.type) { @@ -16401,13 +17344,13 @@ private: return component<string_type>::walk_control::skip; case tag_type::partial: if ((var = ctx.ctx.get_partial(tag.name)) != nullptr && (var->is_partial() || var->is_string())) { - const auto partial_result = var->is_partial() ? var->partial_value()() : var->string_value(); + const auto& partial_result = var->is_partial() ? var->partial_value()() : var->string_value(); basic_mustache tmpl{partial_result}; tmpl.set_custom_escape(escape_); if (!tmpl.is_valid()) { error_message_ = tmpl.error_message(); } else { - tmpl.render(handler, ctx); + tmpl.render(handler, ctx, false); if (!tmpl.is_valid()) { error_message_ = tmpl.error_message(); } @@ -16423,7 +17366,7 @@ private: default: break; } - + return component<string_type>::walk_control::walk; } @@ -16432,7 +17375,7 @@ private: unescape, optional, }; - + bool render_lambda(const render_handler& handler, const basic_data<string_type>* var, context_internal<string_type>& ctx, render_lambda_escape escape, const string_type& text, bool parse_with_same_context) { const typename basic_renderer<string_type>::type2 render2 = [this, &ctx, parse_with_same_context, escape](const string_type& text, bool escaped) { const auto process_template = [this, &ctx, escape, escaped](basic_mustache& tmpl) -> string_type { @@ -16440,7 +17383,8 @@ private: error_message_ = tmpl.error_message(); return {}; } - const string_type str{tmpl.render(ctx)}; + context_internal<string_type> render_ctx{ctx.ctx}; // start a new line_buffer + const auto str = tmpl.render(render_ctx); if (!tmpl.is_valid()) { error_message_ = tmpl.error_message(); return {}; @@ -16473,17 +17417,18 @@ private: }; if (var->is_lambda2()) { const basic_renderer<string_type> renderer{render, render2}; - handler(var->lambda2_value()(text, renderer)); + render_result(ctx, var->lambda2_value()(text, renderer)); } else { - handler(render(var->lambda_value()(text))); + render_current_line(handler, ctx, nullptr); + render_result(ctx, render(var->lambda_value()(text))); } return error_message_.empty(); } - + bool render_variable(const render_handler& handler, const basic_data<string_type>* var, context_internal<string_type>& ctx, bool escaped) { if (var->is_string()) { - const auto varstr = var->string_value(); - handler(escaped ? escape_(varstr) : varstr); + const auto& varstr = var->string_value(); + render_result(ctx, escaped ? escape_(varstr) : varstr); } else if (var->is_lambda()) { const render_lambda_escape escape_opt = escaped ? render_lambda_escape::escape : render_lambda_escape::unescape; return render_lambda(handler, var, ctx, escape_opt, {}, false); @@ -16503,14 +17448,32 @@ private: }; if (var && var->is_non_empty_list()) { for (const auto& item : var->list_value()) { + // account for the section begin tag + ctx.line_buffer.contained_section_tag = true; + const context_pusher<string_type> ctxpusher{ctx, &item}; incomp.walk_children(callback); + + // ctx may have been cleared. account for the section end tag + ctx.line_buffer.contained_section_tag = true; } } else if (var) { + // account for the section begin tag + ctx.line_buffer.contained_section_tag = true; + const context_pusher<string_type> ctxpusher{ctx, var}; incomp.walk_children(callback); + + // ctx may have been cleared. account for the section end tag + ctx.line_buffer.contained_section_tag = true; } else { + // account for the section begin tag + ctx.line_buffer.contained_section_tag = true; + incomp.walk_children(callback); + + // ctx may have been cleared. account for the section end tag + ctx.line_buffer.contained_section_tag = true; } } @@ -17226,7 +18189,7 @@ inline int32_t cluon_replay(int32_t argc, char **argv) { auto commandlineArguments = cluon::getCommandlineArguments(argc, argv); if (1 == argc) { std::cerr << PROGRAM << " replays a .rec file into an OpenDaVINCI session or to stdout; if playing back to an OD4Session using parameter --cid, you can specify the optional parameter --stdout to also playback to stdout; --keeprunning keeps " << PROGRAM << " open at the end of a recording file." << std::endl; - std::cerr << "Usage: " << PROGRAM << " [--cid=<OpenDaVINCI session> [--stdout] [--keeprunning]] recording.rec" << std::endl; + std::cerr << "Usage: " << PROGRAM << " [--cid=<OpenDaVINCI session> [--stdout] [--keeprunning]] [--nodelay] recording.rec" << std::endl; std::cerr << "Example: " << PROGRAM << " --cid=111 file.rec" << std::endl; std::cerr << " " << PROGRAM << " --cid=111 --stdout file.rec" << std::endl; std::cerr << " " << PROGRAM << " file.rec" << std::endl; @@ -17235,6 +18198,7 @@ inline int32_t cluon_replay(int32_t argc, char **argv) { else { const bool playBackToStdout = ( (0 != commandlineArguments.count("stdout")) || (0 == commandlineArguments.count("cid")) ); const bool keepRunning = (0 != commandlineArguments.count("keeprunning")); + const bool noDelay = (0 != commandlineArguments.count("nodelay")); std::string recFile; for (auto e : commandlineArguments) { @@ -17296,7 +18260,7 @@ inline int32_t cluon_replay(int32_t argc, char **argv) { } } constexpr bool AUTOREWIND{false}; - constexpr bool THREADING{true}; + const bool THREADING{!noDelay}; cluon::Player player(recFile, AUTOREWIND, THREADING); player.setPlayerListener([&playerStatusUpdate, &playerStatusMutex, &playerStatus](cluon::data::PlayerStatus &&ps){ { @@ -17375,18 +18339,18 @@ inline int32_t cluon_replay(int32_t argc, char **argv) { std::lock_guard<std::mutex> lck(playerCommandMutex); if ( (playerCommand.command() == 1) || (playerCommand.command() == 2) ) { play = !(2 == playerCommand.command()); // LCOV_EXCL_LINE - std::clog << PROGRAM << ": Change state: " << +playerCommand.command() << ", play = " << play << std::endl; + std::cerr << PROGRAM << ": Change state: " << +playerCommand.command() << ", play = " << play << std::endl; } if (3 == playerCommand.command()) { - std::clog << PROGRAM << ": Change state: " << +playerCommand.command() << ", seekTo: " << playerCommand.seekTo() << std::endl; + std::cerr << PROGRAM << ": Change state: " << +playerCommand.command() << ", seekTo: " << playerCommand.seekTo() << std::endl; player.seekTo(playerCommand.seekTo()); } if (4 == playerCommand.command()) { play = false; step = true; - std::clog << PROGRAM << ": Change state: " << +playerCommand.command() << ", play = " << play << std::endl; + std::cerr << PROGRAM << ": Change state: " << +playerCommand.command() << ", play = " << play << std::endl; } playCommandUpdate = false; @@ -17404,11 +18368,15 @@ inline int32_t cluon_replay(int32_t argc, char **argv) { std::cout << cluon::serializeEnvelope(std::move(e)); std::cout.flush(); } - std::this_thread::sleep_for(std::chrono::duration<int32_t, std::micro>(player.delay())); + if (!noDelay) { + std::this_thread::sleep_for(std::chrono::duration<int32_t, std::micro>(player.delay())); + } } } else { - std::this_thread::sleep_for(std::chrono::duration<int32_t, std::milli>(100)); // LCOV_EXCL_LINE + if (!noDelay) { + std::this_thread::sleep_for(std::chrono::duration<int32_t, std::milli>(100)); // LCOV_EXCL_LINE + } } // LCOV_EXCL_LINE // Reset step. @@ -17461,13 +18429,14 @@ int32_t main(int32_t argc, char **argv) { //#include "cluon/MetaMessage.hpp" //#include "cluon/MessageParser.hpp" //#include "cluon/OD4Session.hpp" +//#include "cluon/TerminateHandler.hpp" #include <chrono> #include <cstdint> #include <fstream> #include <iomanip> #include <iostream> -#include <map> +#include <unordered_map> #include <mutex> #include <sstream> #include <string> @@ -17529,54 +18498,83 @@ inline int32_t cluon_livefeed(int32_t argc, char **argv) { } std::mutex mapOfLastEnvelopesMutex; - std::map<int32_t, std::map<uint32_t, cluon::data::Envelope> > mapOfLastEnvelopes; + std::unordered_map<int32_t, std::unordered_map<uint32_t, cluon::data::Envelope, cluon::UseUInt32ValueAsHashKey>, cluon::UseUInt32ValueAsHashKey> mapOfLastEnvelopes; + std::unordered_map<int32_t, std::unordered_map<uint32_t, float>, cluon::UseUInt32ValueAsHashKey> mapOfUpdateRates; cluon::OD4Session od4Session(static_cast<uint16_t>(std::stoi(commandlineArguments["cid"])), - [&](cluon::data::Envelope &&envelope) noexcept { + [&mapOfLastEnvelopesMutex, &mapOfLastEnvelopes, &mapOfUpdateRates](cluon::data::Envelope &&envelope) noexcept { std::lock_guard<std::mutex> lck(mapOfLastEnvelopesMutex); - // Update mapping for tupel (dataType, senderStamp) --> Envelope. - std::map<uint32_t, cluon::data::Envelope> entry = mapOfLastEnvelopes[envelope.dataType()]; - entry[envelope.senderStamp()] = envelope; - mapOfLastEnvelopes[envelope.dataType()] = entry; - - clearScreen(); - - const auto LAST_TIME_POINT{envelope.received().seconds() * 1000 * 1000 + envelope.received().microseconds()}; + int64_t lastTimeStamp{0}; + int64_t currentTimeStamp{0}; + { + // Update mapping for tupel (dataType, senderStamp) --> Envelope. + auto entry = mapOfLastEnvelopes[envelope.dataType()]; + if (0 != entry.count(envelope.senderStamp())) { + lastTimeStamp = cluon::time::toMicroseconds(entry[envelope.senderStamp()].sampleTimeStamp()); // LCOV_EXCL_LINE + } + currentTimeStamp = cluon::time::toMicroseconds(envelope.sampleTimeStamp()); // LCOV_EXCL_LINE + if (currentTimeStamp != lastTimeStamp) { + entry[envelope.senderStamp()] = envelope; // LCOV_EXCL_LINE + mapOfLastEnvelopes[envelope.dataType()] = entry; // LCOV_EXCL_LINE + } + } + if (currentTimeStamp != lastTimeStamp) { + // Update mapping for tupel (dataType, senderStamp) --> deltaToLastEnvelope. + auto entry = mapOfUpdateRates[envelope.dataType()]; + + float average{0}; + if (0 != entry.count(envelope.senderStamp())) { + average = entry[envelope.senderStamp()]; // LCOV_EXCL_LINE + float freq = (static_cast<float>(currentTimeStamp - lastTimeStamp))/(1000.0f*1000.0f); // LCOV_EXCL_LINE + average = (1.0f/freq)*0.1f + 0.9f*average; // LCOV_EXCL_LINE + } + entry[envelope.senderStamp()] = average; + mapOfUpdateRates[envelope.dataType()] = entry; + } + }); - uint8_t y = 1; - const uint8_t x = 1; - for (auto e : mapOfLastEnvelopes) { - for (auto ee : e.second) { - auto env = ee.second; - std::stringstream sstr; + if (od4Session.isRunning()) { + od4Session.timeTrigger(5, [&mapOfLastEnvelopesMutex, &mapOfLastEnvelopes, &mapOfUpdateRates, &scopeOfMetaMessages, &od4Session](){ + std::lock_guard<std::mutex> lck(mapOfLastEnvelopesMutex); + + if (!mapOfLastEnvelopes.empty()) { + clearScreen(); + + uint8_t y = 1; + const uint8_t x = 1; + for (auto e : mapOfLastEnvelopes) { + for (auto ee : e.second) { + auto env = ee.second; + std::stringstream sstr; + + float freq{0}; + if ( (0 < mapOfUpdateRates.count(ee.second.dataType())) && (0 < mapOfUpdateRates[ee.second.dataType()].count(ee.second.senderStamp())) ) { + freq = mapOfUpdateRates[ee.second.dataType()][ee.second.senderStamp()]; + } - sstr << "Envelope: " << std::setfill(' ') << std::setw(5) << env.dataType() << std::setw(0) << "/" << env.senderStamp() << "; " << "sent: " << formatTimeStamp(env.sent()) << "; sample: " << formatTimeStamp(env.sampleTimeStamp()); - if (scopeOfMetaMessages.count(env.dataType()) > 0) { - sstr << "; " << scopeOfMetaMessages[env.dataType()].messageName(); - } - else { - sstr << "; unknown data type"; - } - sstr << std::endl; + sstr << "Envelope: " << std::setfill(' ') << std::setw(5) << env.dataType() << std::setw(0) << "/" << env.senderStamp() << "; " << (static_cast<float>(static_cast<uint32_t>(freq*100.0f))/100.f) << " Hz; " << "sent: " << formatTimeStamp(env.sent()) << "; sample: " << formatTimeStamp(env.sampleTimeStamp()); + if (scopeOfMetaMessages.count(env.dataType()) > 0) { + sstr << "; " << scopeOfMetaMessages[env.dataType()].messageName(); + } + else { + sstr << "; unknown data type"; + } + sstr << std::endl; - const auto AGE{LAST_TIME_POINT - (env.received().seconds() * 1000 * 1000 + env.received().microseconds())}; + const auto AGE{cluon::time::deltaInMicroseconds(cluon::time::now(), env.received())}; - Color c = Color::DEFAULT; - if (AGE <= 2 * 1000 * 1000) { c = Color::GREEN; } - if (AGE > 2 * 1000 * 1000 && AGE <= 5 * 1000 * 1000) { c = Color::YELLOW; } - if (AGE > 5 * 1000 * 1000) { c = Color::RED; } + Color c = Color::DEFAULT; + if (AGE <= 2 * 1000 * 1000) { c = Color::GREEN; } + if (AGE > 2 * 1000 * 1000 && AGE <= 5 * 1000 * 1000) { c = Color::YELLOW; } + if (AGE > 5 * 1000 * 1000) { c = Color::RED; } - writeText(c, y++, x, sstr.str()); + writeText(c, y++, x, sstr.str()); + } + } } - } - }); - - if (od4Session.isRunning()) { - using namespace std::literals::chrono_literals; // NOLINT - while (od4Session.isRunning()) { - std::this_thread::sleep_for(1s); - } + return od4Session.isRunning(); + }); retVal = 0; } @@ -17749,7 +18747,7 @@ inline int32_t cluon_rec2csv(int32_t argc, char **argv) { // Extract timestamps. std::vector<std::string> timeStampsWithHeader; { - // Skip senderStamp (as it is in file name) and serialzedData. + // Skip senderStamp (as it is in file name) and serializedData. cluon::ToCSVVisitor csv(';', true, { {1,false}, {2,false}, {3,true}, {4,true}, {5,true}, {6,false} }); env.accept(csv); timeStampsWithHeader = stringtoolbox::split(csv.csv(), '\n'); @@ -17766,8 +18764,8 @@ inline int32_t cluon_rec2csv(int32_t argc, char **argv) { // Keep track of buffer sizes. if (mapOfEntriesSizes[KEY] > TEN_MB) { - std::cerr << argv[0] << ": Buffer for '" << KEY << "' has consumed " << mapOfEntriesSizes[KEY] << "/" << TEN_MB << " bytes; dumping data to disk."<< std::endl; - fileWriter(); + std::cerr << argv[0] << ": Buffer for '" << KEY << "' has consumed " << mapOfEntriesSizes[KEY] << "/" << TEN_MB << " bytes; dumping data to disk."<< std::endl; // LCOV_EXCL_LINE + fileWriter(); // LCOV_EXCL_LINE } } } diff --git a/src/opendlv-message-standard-1.0.odvd b/src/opendlv-message-standard-1.0.odvd new file mode 100644 index 0000000..f850965 --- /dev/null +++ b/src/opendlv-message-standard-1.0.odvd @@ -0,0 +1,429 @@ +/* + * Copyright (C) 2024 OpenDLV + */ + +message opendlv.sim.KinematicState [id = 1002] { + float vx [id = 1]; + float vy [id = 2]; + float vz [id = 3]; + float rollRate [id = 4]; + float pitchRate [id = 5]; + float yawRate [id = 6]; +} + +message opendlv.sim.Frame [id = 1003] { + float x [id = 1]; + float y [id = 2]; + float z [id = 3]; + float qx [id = 4]; + float qy [id = 5]; + float qz [id = 6]; + float qw [id = 7]; +} + +message opendlv.sim.Command [id = 1004] { + string command [id = 1]; +} + +message opendlv.meta.SampleProperty [id = 1010] { + string key [id = 1]; + string value [id = 2]; +} + +message opendlv.meta.StreamProperty [id = 1011] { + string key [id = 1]; + string value [id = 2]; +} + +message opendlv.meta.SubFrame [id = 1014] { + float x [id = 1]; + float y [id = 2]; + float z [id = 3]; + float qx [id = 4]; + float qy [id = 5]; + float qz [id = 6]; + float qw [id = 7]; +} + +message opendlv.meta.CalibrationVisualDistortion [id = 1015] { + float k1 [id = 1]; + float k2 [id = 2]; + float k3 [id = 3]; + float p1 [id = 4]; + float p2 [id = 5]; +} + +message opendlv.meta.CalibrationCameraIntrinsic [id = 1016] { + float fx [id = 1]; + float fy [id = 2]; + float cx [id = 3]; + float cy [id = 4]; +} + +message opendlv.meta.CalibrationExtrinsic [id = 1017] { + float x [id = 1]; + float y [id = 2]; + float z [id = 3]; + float qx [id = 4]; + float qy [id = 5]; + float qz [id = 6]; + float qw [id = 7]; + uint32 frameId [id = 8]; +} + +message opendlv.meta.FrameShapeShm [id = 1020] { + string format [id = 1]; + string location [id = 2]; +} + +message opendlv.meta.FrameShape [id = 1021] { + string format [id = 1]; + bytes data [id = 2]; +} + +message opendlv.proxy.StepReading [id = 1029] { + int32 steps [id = 1]; +} + +message opendlv.proxy.AccelerationReading [id = 1030] { + float accelerationX [id = 1]; + float accelerationY [id = 2]; + float accelerationZ [id = 3]; +} + +message opendlv.proxy.AngularVelocityReading [id = 1031] { + float angularVelocityX [id = 1]; + float angularVelocityY [id = 2]; + float angularVelocityZ [id = 3]; +} + +message opendlv.proxy.MagneticFieldReading [id = 1032] { + float magneticFieldX [id = 1]; + float magneticFieldY [id = 2]; + float magneticFieldZ [id = 3]; +} + +message opendlv.proxy.AltitudeReading [id = 1033] { + float altitude [id = 1]; +} + +message opendlv.proxy.PressureReading [id = 1034] { + float pressure [id = 1]; +} + +message opendlv.proxy.TemperatureReading [id = 1035] { + float temperature [id = 1]; +} + +message opendlv.proxy.TorqueReading [id = 1036] { + float torque [id = 1]; +} + +message opendlv.proxy.VoltageReading [id = 1037] { + float voltage [id = 1]; +} + +message opendlv.proxy.AngleReading [id = 1038] { + float angle [id = 1]; +} + +message opendlv.proxy.DistanceReading [id = 1039] { + float distance [id = 1]; +} + +message opendlv.proxy.SwitchStateReading [id = 1040] { + int16 state [id = 1]; +} + +message opendlv.proxy.PedalPositionReading [id = 1041] { + float position [id = 1]; +} + +message opendlv.proxy.ElectricCurrentReading [id = 1042] { + float electricCurrent [id = 1]; +} + +message opendlv.proxy.StateOfChargeReading [id = 1043] { + float stateOfCharge [id = 1]; +} + +message opendlv.proxy.GroundSteeringReading [id = 1045] { + float groundSteering [id = 1]; +} + +message opendlv.proxy.GroundSpeedReading [id = 1046] { + float groundSpeed [id = 1]; +} + +message opendlv.proxy.AxleAngularVelocityReading [id = 1047] { + float axleAngularVelocity [id = 1]; +} + +message opendlv.proxy.StrokeLengthReading [id = 1048] { + float strokeLength [id = 1]; +} + +message opendlv.proxy.WeightReading [id = 1050] { + float weight [id = 1]; +} + +message opendlv.proxy.GeodeticWgs84HeadingReading [id = 1051] { + float northHeading [id = 1]; +} + +message opendlv.proxy.GeodeticWgs84PositionReading [id = 19] { + double latitude [id = 1]; + double longitude [id = 3]; +} + +message opendlv.proxy.ImageReadingShm [id = 1054] { + string format [id = 1]; + uint32 width [id = 2]; + uint32 height [id = 3]; + string location [id = 4]; + uint32 sampleId [id = 5]; +} + +message opendlv.proxy.ImageReading [id = 1055] { + string format [id = 1]; + uint32 width [id = 2]; + uint32 height [id = 3]; + bytes data [id = 4]; + uint32 sampleId [id = 5]; +} + +message opendlv.proxy.RemoteMessageReading [id = 1056] { + string address [id = 1]; + string message [id = 2]; +} + +message opendlv.proxy.PointCloudAngularLayeredReadingShm [id = 1060] { + float layerAngle [id = 1]; + string format [id = 2]; + uint32 count [id = 3]; + string location [id = 4]; + uint32 sampleId [id = 5]; +} + +message opendlv.proxy.PointCloudAngularLayeredReading [id = 1061] { + float layerAngle [id = 1]; + string format [id = 2]; + uint32 count [id = 3]; + bytes data [id = 4]; + uint32 sampleId [id = 5]; +} + +message opendlv.proxy.StepRequest [id = 1079] { + int32 steps [id = 1]; +} + +message opendlv.proxy.PressureRequest [id = 1080] { + float pressure [id = 1]; +} + +message opendlv.proxy.TemperatureRequest [id = 1081] { + float temperature [id = 1]; +} + +message opendlv.proxy.TorqueRequest [id = 1082] { + float torque [id = 1]; +} + +message opendlv.proxy.VoltageRequest [id = 1083] { + float voltage [id = 1]; +} + +message opendlv.proxy.AngleRequest [id = 1084] { + float angle [id = 1]; +} + +message opendlv.proxy.SwitchStateRequest [id = 1085] { + int16 state [id = 1]; +} + +message opendlv.proxy.PedalPositionRequest [id = 1086] { + float position [id = 1]; +} + +message opendlv.proxy.PulseWidthModulationRequest [id = 1087] { + uint32 dutyCycleNs [id = 1]; +} + +message opendlv.proxy.StrokeLengthRequest [id = 1088] { + float strokeLength [id = 1]; +} + +message opendlv.proxy.GroundMotionRequest [id = 1089] { + float vx [id = 1]; + float vy [id = 2]; + float vz [id = 3]; + float rollRate [id = 4]; + float pitchRate [id = 5]; + float yawRate [id = 6]; +} + +message opendlv.proxy.GroundSteeringRequest [id = 1090] { + float groundSteering [id = 1]; +} + +message opendlv.proxy.GroundSpeedRequest [id = 1091] { + float groundSpeed [id = 1]; +} + +message opendlv.proxy.GroundAccelerationRequest [id = 1092] { + float groundAcceleration [id = 1]; +} + +message opendlv.proxy.GroundDecelerationRequest [id = 1093] { + float groundDeceleration [id = 1]; +} + +message opendlv.proxy.AxleAngularVelocityRequest [id = 1094] { + float axleAngularVelocity [id = 1]; +} + +message opendlv.proxy.RemoteMessageRequest [id = 1095] { + string address [id = 1]; + string message [id = 2]; +} + +message opendlv.proxy.LedRequest [id = 1096] { + string format [id = 1]; + bytes data [id = 2]; +} + +message opendlv.system.SignalStatusMessage [id = 1100] { + int32 code [id = 1]; + string description [id = 2]; +} + +message opendlv.system.SystemStatusMessage [id = 1101] { + int32 code [id = 1]; + string description [id = 2]; +} + +message opendlv.system.NetworkStatusMessage [id = 1102] { + int32 code [id = 1]; + string description [id = 2]; +} + +message opendlv.system.LogMessage [id = 1103] { + uint8 level [default = 6, id = 1]; + string description [id = 2]; +} + +message opendlv.system.Command [id = 1104] { + string command [id = 1]; +} + +message opendlv.logic.sensation.Direction [id = 1110] { + float azimuthAngle [id = 1]; + float zenithAngle [id = 2]; +} + +message opendlv.logic.sensation.Point [id = 1111] { + float azimuthAngle [id = 1]; + float zenithAngle [id = 2]; + float distance [id = 3]; +} + +message opendlv.logic.sensation.Geolocation [id = 1116] { + double latitude [id = 1]; + double longitude [id = 2]; + float altitude [id = 3]; + float heading [id = 4]; +} + +message opendlv.logic.sensation.Motion [id = 1117] { + float vx [id = 1]; + float vy [id = 2]; + float vz [id = 3]; + float rollRate [id = 4]; + float pitchRate [id = 5]; + float yawRate [id = 6]; +} + +message opendlv.logic.sensation.Orientation [id = 1119] { + float qx [id = 1]; + float qy [id = 2]; + float qz [id = 3]; + float qw [id = 4]; +} + +message opendlv.logic.perception.DetectionType [id = 1130] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + uint32 type [id = 3]; +} + +message opendlv.logic.perception.DetectionProperty [id = 1131] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + string property [id = 3]; +} + +message opendlv.logic.perception.DetectionDirection [id = 1132] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + float azimuthAngle [id = 3]; + float zenithAngle [id = 4]; +} + +message opendlv.logic.perception.DetectionDistance [id = 1133] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + float distance [id = 3]; +} + +message opendlv.logic.perception.DetectionBoundingBox [id = 1134] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + float x [id = 3]; + float y [id = 4]; + float width [id = 5]; + float height [id = 6]; +} + +message opendlv.logic.perception.DetectionPosition [id = 1135] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + float x [id = 3]; + float y [id = 4]; + float z [id = 5]; +} + +message opendlv.logic.perception.DetectionSurface [id = 1136] { + uint32 sampleId [id = 1]; + uint32 detectionId [id = 2]; + string format [id = 3]; + bytes data [id = 4]; +} + +message opendlv.logic.action.AimDirection [id = 1171] { + float azimuthAngle [id = 1]; + float zenithAngle [id = 2]; +} + +message opendlv.logic.action.AimPoint [id = 1172] { + float azimuthAngle [id = 1]; + float zenithAngle [id = 2]; + float distance [id = 3]; +} + +message opendlv.logic.action.PreviewPoint [id = 1173] { + float azimuthAngle [id = 1]; + float zenithAngle [id = 2]; + float distance [id = 3]; +} + +message opendlv.logic.action.GlobalPath [id = 1180] { + string format [id = 1]; + uint32 count [id = 2]; + bytes data [id = 3]; +} + +message opendlv.logic.action.LocalPath [id = 1181] { + string format [id = 1]; + uint32 count [id = 2]; + bytes data [id = 3]; +} diff --git a/src/opendlv-standard-message-set-v0.9.6.odvd b/src/opendlv-standard-message-set-v0.9.6.odvd deleted file mode 100644 index fac1414..0000000 --- a/src/opendlv-standard-message-set-v0.9.6.odvd +++ /dev/null @@ -1,366 +0,0 @@ -/* - * Copyright (C) 2018 Chalmers Revere - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -message opendlv.sim.Frame [id = 1001] { - float x [id = 1]; - float y [id = 2]; - float z [id = 3]; - float roll [id = 4]; - float pitch [id = 5]; - float yaw [id = 6]; -} - -message opendlv.sim.KinematicState [id = 1002] { - float vx [id = 1]; - float vy [id = 2]; - float vz [id = 3]; - float rollRate [id = 4]; - float pitchRate [id = 5]; - float yawRate [id = 6]; -} - -message opendlv.body.ComponentInfo [id = 1021] { - string description [id = 1]; - float x [id = 2]; - float y [id = 3]; - float z [id = 4]; -} - -message opendlv.body.ActuatorInfo [id = 1022] { - string description [id = 1]; - float x [id = 2]; - float y [id = 3]; - float z [id = 4]; - uint32 signalId [id = 5]; - float minValue [id = 6]; - float maxValue [id = 7]; -} - -message opendlv.body.SensorInfo [id = 1023] { - string description [id = 1]; - float x [id = 2]; - float y [id = 3]; - float z [id = 4]; - uint32 signalId [id = 5]; - float accuracyStd [id = 6]; - uint16 minFrequency [id = 7]; -} - -message opendlv.body.SignalInfo [id = 1024] { - string description [id = 1]; - uint32 signalId [id = 2]; - float accuracyStd [id = 3]; - uint16 minFrequency [id = 4]; -} - -message opendlv.proxy.AccelerationReading [id = 1030] { - float accelerationX [id = 1]; - float accelerationY [id = 2]; - float accelerationZ [id = 3]; -} - -message opendlv.proxy.AngularVelocityReading [id = 1031] { - float angularVelocityX [id = 1]; - float angularVelocityY [id = 2]; - float angularVelocityZ [id = 3]; -} - -message opendlv.proxy.MagneticFieldReading [id = 1032] { - float magneticFieldX [id = 1]; - float magneticFieldY [id = 2]; - float magneticFieldZ [id = 3]; -} - -message opendlv.proxy.AltitudeReading [id = 1033] { - float altitude [id = 1]; -} - -message opendlv.proxy.PressureReading [id = 1034] { - float pressure [id = 1]; -} - -message opendlv.proxy.TemperatureReading [id = 1035] { - float temperature [id = 1]; -} - -message opendlv.proxy.TorqueReading [id = 1036] { - float torque [id = 1]; -} - -message opendlv.proxy.VoltageReading [id = 1037] { - float voltage [id = 1]; -} - -message opendlv.proxy.AngleReading [id = 1038] { - float angle [id = 1]; -} - -message opendlv.proxy.DistanceReading [id = 1039] { - float distance [id = 1]; -} - -message opendlv.proxy.SwitchStateReading [id = 1040] { - int16 state [id = 1]; -} - -message opendlv.proxy.PedalPositionReading [id = 1041] { - float position [id = 1]; -} - -message opendlv.proxy.GroundSteeringReading [id = 1045] { - float groundSteering [id = 1]; -} - -message opendlv.proxy.GroundSpeedReading [id = 1046] { - float groundSpeed [id = 1]; -} - -message opendlv.proxy.WheelSpeedReading [id = 1047] { - float wheelSpeed [id = 1]; -} - -message opendlv.proxy.WeightReading [id = 1050] { - float weight [id = 1]; -} - -message opendlv.proxy.GeodeticHeadingReading [id = 1051] { - float northHeading [id = 1]; -} - -message opendlv.proxy.GeodeticWgs84Reading [id = 19] { - double latitude [id = 1]; - double longitude [id = 3]; -} - -message opendlv.proxy.ImageReading [id = 1055] { - string fourcc [id = 1]; - uint32 width [id = 2]; - uint32 height [id = 3]; - bytes data [id = 4]; -} - -message opendlv.proxy.ImageReadingShared [id = 14] { - string name [id = 1]; - uint32 size [id = 2]; - uint32 width [id = 3]; - uint32 height [id = 4]; - uint32 bytesPerPixel [id = 5]; -} - -message opendlv.proxy.PointCloudReading [id = 49] { - float startAzimuth [id = 1]; - float endAzimuth [id = 2]; - uint8 entriesPerAzimuth [id = 3]; - bytes distances [id = 4]; - uint8 numberOfBitsForIntensity [id = 5]; -} - -message opendlv.proxy.PointCloudReadingShared [id = 28] { - string name [id = 1]; - uint32 size [id = 2]; - uint32 width [id = 3]; - uint32 height [id = 4]; - uint8 numberOfComponentsPerPoint [id = 5]; -} - -// V2xReading? - - -message opendlv.proxy.PressureRequest [id = 1080] { - float pressure [id = 1]; -} - -message opendlv.proxy.TemperatureRequest [id = 1081] { - float temperature [id = 1]; -} - -message opendlv.proxy.TorqueRequest [id = 1082] { - float torque [id = 1]; -} - -message opendlv.proxy.VoltageRequest [id = 1083] { - float voltage [id = 1]; -} - -message opendlv.proxy.AngleRequest [id = 1084] { - float angle [id = 1]; -} - -message opendlv.proxy.SwitchStateRequest [id = 1085] { - int16 state [id = 1]; -} - -message opendlv.proxy.PedalPositionRequest [id = 1086] { - float position [id = 1]; -} - -message opendlv.proxy.PulseWidthModulationRequest [id = 1087] { - uint32 dutyCycleNs [id = 1]; -} - -message opendlv.proxy.GroundSteeringRequest [id = 1090] { - float groundSteering [id = 1]; -} - -message opendlv.proxy.GroundSpeedRequest [id = 1091] { - float groundSpeed [id = 1]; -} - -message opendlv.proxy.GroundAccelerationRequest [id = 1092] { - float groundAcceleration [id = 1]; -} - -message opendlv.proxy.GroundDecelerationRequest [id = 1093] { - float groundDeceleration [id = 1]; -} - -message opendlv.proxy.WheelSpeedRequest [id = 1094] { - float wheelSpeed [id = 1]; -} - -// V2xRequest? - - -message opendlv.system.SignalStatusMessage [id = 1100] { - int32 code [id = 1]; - string description [id = 2]; -} - -message opendlv.system.SystemOperationState [id = 1101] { - int32 code [id = 1]; - string description [id = 2]; -} - -message opendlv.system.NetworkStatusMessage [id = 1102] { - int32 code [id = 1]; - string description [id = 2]; -} - - - -message opendlv.logic.sensation.Direction [id = 1110] { - float azimuthAngle [id = 1]; - float zenithAngle [id = 2]; -} - -message opendlv.logic.sensation.Point [id = 1111] { - float azimuthAngle [id = 1]; - float zenithAngle [id = 2]; - float distance [id = 3]; -} - -message opendlv.logic.sensation.Geolocation [id = 1116] { - double latitude [id = 1]; - double longitude [id = 2]; - float altitude [id = 3]; - float heading [id = 4]; -} - -message opendlv.logic.sensation.Equilibrioception [id = 1017] { - float vx [id = 1]; - float vy [id = 2]; - float vz [id = 3]; - float rollRate [id = 4]; - float pitchRate [id = 5]; - float yawRate [id = 6]; -} - - - -message opendlv.logic.perception.Object [id = 1130] { - uint32 objectId [id = 1]; -} - -message opendlv.logic.perception.ObjectType [id = 1131] { - uint32 objectId [id = 1]; - uint32 type [id = 2]; -} - -message opendlv.logic.perception.ObjectProperty [id = 1132] { - uint32 objectId [id = 1]; - string property [id = 2]; -} - -message opendlv.logic.perception.ObjectDirection [id = 1133] { - uint32 objectId [id = 1]; - float azimuthAngle [id = 2]; - float zenithAngle [id = 3]; -} - -message opendlv.logic.perception.ObjectDistance [id = 1134] { - uint32 objectId [id = 1]; - float distance [id = 2]; -} - -message opendlv.logic.perception.ObjectAngularBlob [id = 1135] { - uint32 objectId [id = 1]; - float width [id = 2]; - float height [id = 3]; -} - -message opendlv.logic.perception.GroundSurface [id = 1140] { - uint32 surfaceId [id = 1]; -} - -message opendlv.logic.perception.GroundSurfaceType [id = 1141] { - uint32 surfaceId [id = 1]; - uint32 type [id = 2]; -} - -message opendlv.logic.perception.GroundSurfaceProperty [id = 1142] { - uint32 surfaceId [id = 1]; - string property [id = 2]; -} - -message opendlv.logic.perception.GroundSurfaceArea [id = 1143] { - uint32 surfaceId [id = 1]; - float x1 [id = 2]; - float y1 [id = 3]; - float x2 [id = 4]; - float y2 [id = 5]; - float x3 [id = 6]; - float y3 [id = 7]; - float x4 [id = 8]; - float y4 [id = 9]; -} - - -message opendlv.logic.action.AimDirection [id = 1171] { - float azimuthAngle [id = 1]; - float zenithAngle [id = 2]; -} - -message opendlv.logic.action.AimPoint [id = 1172] { - float azimuthAngle [id = 1]; - float zenithAngle [id = 2]; - float distance [id = 3]; -} - -message opendlv.logic.action.PreviewPoint [id = 1173] { - float azimuthAngle [id = 1]; - float zenithAngle [id = 2]; - float distance [id = 3]; -} - -message opendlv.logic.cognition.GroundSteeringLimit [id = 1191] { - float steeringLimit [id = 1]; -} - -message opendlv.logic.cognition.GroundSpeedLimit [id = 1192] { - float speedLimit [id = 1]; -} diff --git a/src/opendlv-statsd.cpp b/src/opendlv-statsd.cpp index 1af54f2..5a96953 100644 --- a/src/opendlv-statsd.cpp +++ b/src/opendlv-statsd.cpp @@ -1,76 +1,74 @@ /* - * Copyright (C) 2019 Christian Berger - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. + * Copyright (C) 2024 OpenDLV */ #include "cluon-complete.hpp" -#include "opendlv-standard-message-set.hpp" +#include "opendlv-message-standard.hpp" #include <cstdint> #include <iostream> #include <memory> #include <mutex> -int32_t main(int32_t argc, char **argv) { - int32_t retCode{1}; - auto commandlineArguments = cluon::getCommandlineArguments(argc, argv); - if ( (0 == commandlineArguments.count("cid")) ) { - std::cerr << argv[0] << " joins a running OD4 session to relay selected messages or message fields at a given samplerate to statsd." << std::endl; - std::cerr << "Usage: " << argv[0] << " --cid=<OD4 session> [--verbose]" << std::endl; - std::cerr << " --cid: CID of the OD4Session to receive messages" << std::endl; - std::cerr << "Example: " << argv[0] << " --cid=111 --verbose" << std::endl; - } - else { - const bool VERBOSE{commandlineArguments.count("verbose") != 0}; +int32_t main(int32_t argc, char **argv) +{ + int32_t retCode{1}; + auto cmd = cluon::getCommandlineArguments(argc, argv); + if ((0 == cmd.count("cid"))) { + std::cerr << argv[0] + << " joins a running OD4 session to relay selected messages or " + "message fields at a given samplerate to statsd." + << std::endl; + std::cerr << "Usage: " << argv[0] << " --cid=<OD4 session> [--verbose]" + << std::endl; + std::cerr << " --cid: CID of the OD4Session to receive messages" + << std::endl; + std::cerr << "Example: " << argv[0] << " --cid=111 --verbose" << std::endl; + } else { + bool const VERBOSE{cmd.count("verbose") != 0}; - // Interface to a running OpenDaVINCI session; here, you can receive messages. - cluon::OD4Session od4{static_cast<uint16_t>(std::stoi(commandlineArguments["cid"]))}; + // Interface to a running OpenDaVINCI session; here, you can receive + // messages. + cluon::OD4Session od4{static_cast<uint16_t>(std::stoi(cmd["cid"]))}; - std::mutex dataMutex; + std::mutex dataMutex; - // TODO: In the following, there is an example to receive WGS84 GPS - // positions in a data triggered way. The type or fields of - // interest needs to be specified by the user as command line - // parameters and subsequently, the corresponding C++ lambdas - // would have to be created and registered. + // TODO: In the following, there is an example to receive WGS84 GPS + // positions in a data triggered way. The type or fields of + // interest needs to be specified by the user as command line + // parameters and subsequently, the corresponding C++ lambdas + // would have to be created and registered. - // Handler to receive a GPS message (realized as C++ lambda). - auto onGPS = [&dataMutex](cluon::data::Envelope &&env){ - // Now, we unpack the cluon::data::Envelope to get the desired WGS84Reading. - opendlv::proxy::GeodeticWgs84Reading gps = cluon::extractMessage<opendlv::proxy::GeodeticWgs84Reading>(std::move(env)); + // Handler to receive a GPS message (realized as C++ lambda). + auto onGPS = [&dataMutex](cluon::data::Envelope &&env) { + // Now, we unpack the cluon::data::Envelope to get the desired + // WGS84Reading. + opendlv::proxy::GeodeticWgs84Reading gps = + cluon::extractMessage<opendlv::proxy::GeodeticWgs84Reading>( + std::move(env)); - { - // TODO: Temporarily store the desired field to be passed on to statsd in the time-trigger below. - } - }; + { + // TODO: Temporarily store the desired field to be passed on to statsd + // in the time-trigger below. + } + }; - // Register our lambda for the message identifier for opendlv::proxy::GeodeticWgs84Reading. - od4.dataTrigger(opendlv::proxy::GeodeticWgs84Reading::ID(), onGPS); + // Register our lambda for the message identifier for + // opendlv::proxy::GeodeticWgs84Reading. + od4.dataTrigger(opendlv::proxy::GeodeticWgs84Reading::ID(), onGPS); - // Time-triggered sender; TODO: reflect the desired sample rate via commandline. - const float ONE_HZ{1.0f}; - od4.timeTrigger(ONE_HZ, [&od4, &dataMutex](){ - { - std::lock_guard<std::mutex> lck(dataMutex); - // TODO: something with the WGS84 reading, i.e. forward to statsd. - } - return od4.isRunning(); - }); - - retCode = 0; - } - return retCode; -} + // Time-triggered sender; TODO: reflect the desired sample rate via + // commandline. + float const ONE_HZ{1.0f}; + od4.timeTrigger(ONE_HZ, [&od4, &dataMutex]() { + { + std::lock_guard<std::mutex> lck(dataMutex); + // TODO: something with the WGS84 reading, i.e. forward to statsd. + } + return od4.isRunning(); + }); + retCode = 0; + } + return retCode; +} \ No newline at end of file -- GitLab