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> &params)
+      : 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> &params_;
 };
 
 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> &params)
+      : 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> &params_;
 };
 
 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> &params)
+      : 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> &params_;
+};
+
+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 &param = 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 = [&current_text, &current_text_position, &sections]() {
             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(&sections.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