From d268b5c7c2e97796efc8ff6ae21d679f4cf1a60d Mon Sep 17 00:00:00 2001 From: Christian Berger Date: Sat, 4 Dec 2021 09:43:19 +0100 Subject: [PATCH] * enabling GH actions Signed-off-by: Christian Berger --- .github/workflows/build-docker-image.yml | 15 + .github/workflows/release.yml | 41 + Dockerfile.amd64 => Dockerfile | 9 +- Dockerfile.aarch64 | 44 - Dockerfile.armhf | 44 - README.md | 11 +- ...0.0.122.hpp => cluon-complete-v0.0.140.hpp | 6252 +++++++++-------- 7 files changed, 3563 insertions(+), 2853 deletions(-) create mode 100644 .github/workflows/build-docker-image.yml create mode 100644 .github/workflows/release.yml rename Dockerfile.amd64 => Dockerfile (87%) delete mode 100644 Dockerfile.aarch64 delete mode 100644 Dockerfile.armhf rename cluon-complete-v0.0.122.hpp => cluon-complete-v0.0.140.hpp (82%) diff --git a/.github/workflows/build-docker-image.yml b/.github/workflows/build-docker-image.yml new file mode 100644 index 0000000..b9c55c4 --- /dev/null +++ b/.github/workflows/build-docker-image.yml @@ -0,0 +1,15 @@ +name: Docker Image CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Build the Docker image + run: docker build . --file Dockerfile --tag my-image-name:$(date +%s) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c88d1e5 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,41 @@ +name: release + +on: + release: + types: [published] + +jobs: + buildx: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to GitHub Container Registry + uses: docker/login-action@v1 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + - + name: Available platforms + run: echo ${{ steps.buildx.outputs.platforms }} + - + name: build and push + id: docker_build_and_push + uses: docker/build-push-action@v2 + with: + file: Dockerfile + push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7 + tags: | + ghcr.io/chrberger/cluon-livefeed:latest + ghcr.io/chrberger/cluon-livefeed:${{ github.event.release.tag_name }} diff --git a/Dockerfile.amd64 b/Dockerfile similarity index 87% rename from Dockerfile.amd64 rename to Dockerfile index 28711d2..ff8de22 100644 --- a/Dockerfile.amd64 +++ b/Dockerfile @@ -1,4 +1,4 @@ -# Copyright (C) 2019 Christian Berger +# Copyright (C) 2021 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 @@ -14,15 +14,16 @@ # along with this program. If not, see . # Part to build cluon-livefeed. -FROM alpine:3.7 as builder +FROM alpine:3.15 as builder MAINTAINER Christian Berger "christian.berger@gu.se" RUN apk update && \ apk --no-cache add \ - g++ + g++ \ + linux-headers ADD . /opt/sources WORKDIR /opt/sources -RUN ln -sf cluon-complete-v0.0.122.hpp cluon-complete.cpp && \ +RUN ln -sf cluon-complete-v0.0.140.hpp cluon-complete.cpp && \ g++ -std=c++14 -Wall -D HAVE_CLUON_LIVEFEED -pthread -s -static -static-libgcc -static-libstdc++ -o /tmp/cluon-livefeed cluon-complete.cpp diff --git a/Dockerfile.aarch64 b/Dockerfile.aarch64 deleted file mode 100644 index 40e83f3..0000000 --- a/Dockerfile.aarch64 +++ /dev/null @@ -1,44 +0,0 @@ -# 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 . - -# Part to build cluon-livefeed. -FROM docker.io/project31/aarch64-alpine-qemu:3.5-7 as builder -MAINTAINER Christian Berger "christian.berger@gu.se" - -RUN [ "cross-build-start" ] - -RUN cat /etc/apk/repositories && \ - echo http://dl-4.alpinelinux.org/alpine/v3.7/main > /etc/apk/repositories && \ - echo http://dl-4.alpinelinux.org/alpine/v3.7/community >> /etc/apk/repositories - -RUN apk update && \ - apk --no-cache add \ - g++ -ADD . /opt/sources -WORKDIR /opt/sources -RUN ln -sf cluon-complete-v0.0.122.hpp cluon-complete.cpp && \ - g++ -std=c++14 -Wall -D HAVE_CLUON_LIVEFEED -pthread -s -static -static-libgcc -static-libstdc++ -o /tmp/cluon-livefeed cluon-complete.cpp - -RUN [ "cross-build-end" ] - - -# Part to deploy cluon-livefeed. -FROM docker.io/project31/aarch64-alpine-qemu:3.5-7 -MAINTAINER Christian Berger "christian.berger@gu.se" - -WORKDIR /usr/bin -COPY --from=builder /tmp/cluon-livefeed . -ENTRYPOINT ["cluon-livefeed"] - diff --git a/Dockerfile.armhf b/Dockerfile.armhf deleted file mode 100644 index c60c3d7..0000000 --- a/Dockerfile.armhf +++ /dev/null @@ -1,44 +0,0 @@ -# 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 . - -# Part to build cluon-livefeed. -FROM pipill/armhf-alpine:edge as builder -MAINTAINER Christian Berger "christian.berger@gu.se" - -RUN [ "cross-build-start" ] - -RUN cat /etc/apk/repositories && \ - echo http://dl-4.alpinelinux.org/alpine/v3.7/main > /etc/apk/repositories && \ - echo http://dl-4.alpinelinux.org/alpine/v3.7/community >> /etc/apk/repositories - -RUN apk update && \ - apk --no-cache add \ - g++ -ADD . /opt/sources -WORKDIR /opt/sources -RUN ln -sf cluon-complete-v0.0.122.hpp cluon-complete.cpp && \ - g++ -std=c++14 -Wall -D HAVE_CLUON_LIVEFEED -pthread -s -static -static-libgcc -static-libstdc++ -o /tmp/cluon-livefeed cluon-complete.cpp - -RUN [ "cross-build-end" ] - - -# Part to deploy cluon-livefeed. -FROM pipill/armhf-alpine:edge -MAINTAINER Christian Berger "christian.berger@gu.se" - -WORKDIR /usr/bin -COPY --from=builder /tmp/cluon-livefeed . -ENTRYPOINT ["cluon-livefeed"] - diff --git a/README.md b/README.md index 75e9469..2973e3b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ `cluon-livefeed` is a microservice for [libcluon](https://github.com/chrberger/libcluon)-based [OD4Sessions](https://github.com/chalmers-revere/opendlv) to display the currently exchanged messages in [`Envelope`](https://github.com/chrberger/libcluon/blob/master/libcluon/resources/cluonDataStructures.odvd#L23-L30) data format. -[![License](https://img.shields.io/badge/license-GPL--3-blue.svg)](https://raw.githubusercontent.com/chrberger/libcluon/master/LICENSE) [![x86_64](https://img.shields.io/badge/platform-x86_64-blue.svg)](https://hub.docker.com/r/chrberger/cluon-livefeed-amd64/tags/) [![armhf](https://img.shields.io/badge/platform-armhf-blue.svg)](https://hub.docker.com/r/chrberger/cluon-livefeed-armhf/tags/) [![aarch64](https://img.shields.io/badge/platform-aarch64-blue.svg)](https://hub.docker.com/r/chrberger/cluon-livefeed-aarch64/tags/) [![multi](https://img.shields.io/badge/platform-multi-blue.svg)](https://hub.docker.com/r/chrberger/cluon-livefeed-multi/tags/) +[![License](https://img.shields.io/badge/license-GPL--3-blue.svg)](https://raw.githubusercontent.com/chrberger/libcluon/master/LICENSE) ## Table of Contents * [Features](#features) @@ -24,21 +24,16 @@ project as it ships its dependencies as part of the source distribution: )](https://www.gnu.org/licenses/gpl-3.0.txt) ## Usage -This microservice is created automatically on changes to this repository via Docker's public registry for: -* [x86_64](https://hub.docker.com/r/chrberger/cluon-livefeed-amd64/tags/) -* [armhf](https://hub.docker.com/r/chrberger/cluon-livefeed-armhf/tags/) -* [aarch64](https://hub.docker.com/r/chrberger/cluon-livefeed-aarch64/tags/) - This microservice is supposed to be used in parallel with a running [OD4Sessions](https://github.com/chalmers-revere/opendlv) with other microservices that exchange messages in [`Envelope`](https://github.com/chrberger/libcluon/blob/master/libcluon/resources/cluonDataStructures.odvd#L23-L30) data format. The purpose of this microservice to display the type and timestamps of the currently exchanged messages on console. It can be used as shown in the following: ``` -docker run --rm -ti --init --net=host chrberger/cluon-livefeed-multi:v0.0.122 --cid=111 +docker run --rm -ti --init --net=host ghcr.io/chrberger/cluon-livefeed:latest --cid=111 ``` Additionally, you can supply a message specification in `.odvd`-file like, for example, the [OpenDLV Standard Message Set](https://github.com/chalmers-revere/opendlv.standard-message-set/blob/master/opendlv.odvd) to dynamically resolve the data types of the exchanged messages. In the following, it is assumed that you have the `.odvd`-file named `example.odvd` residing in the current working directory: ``` -docker run --rm -ti --init --net=host -v $PWD:/opt chrberger/cluon-livefeed-multi:v0.0.122 --cid=111 --odvd=/opt/example.odvd +docker run --rm -ti --init --net=host -v $PWD:/opt ghcr.io/chrberger/cluon-livefeed:latest --cid=111 --odvd=/opt/example.odvd ``` ## License diff --git a/cluon-complete-v0.0.122.hpp b/cluon-complete-v0.0.140.hpp similarity index 82% rename from cluon-complete-v0.0.122.hpp rename to cluon-complete-v0.0.140.hpp index 93b3d01..918ced1 100644 --- a/cluon-complete-v0.0.122.hpp +++ b/cluon-complete-v0.0.140.hpp @@ -1,6 +1,6 @@ // This is an auto-generated header-only single-file distribution of libcluon. -// Date: Sat, 03 Aug 2019 15:58:53 +0200 -// Version: 0.0.122 +// Date: Thu, 04 Feb 2021 12:20:57 +0000 +// Version: 0.0.140 // // // Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. @@ -477,202 +477,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 #include +#include #include #include #include #include #include +#include #include #include #include #include +#include #include #include #include +#if PEGLIB_USE_STD_ANY +#include +#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 { /*----------------------------------------------------------------------------- * 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 +auto any_cast(Args &&... args) + -> decltype(std::any_cast(std::forward(args)...)) { + return std::any_cast(std::forward(args)...); +} +#else +class any { +public: + any() = default; - template - any(const T& value) : content_(new holder(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 any(const T &value) : content_(new holder(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::value, std::nullptr_t>::type = nullptr - > - T& get() { - if (!content_) { - throw std::bad_cast(); - } - auto p = dynamic_cast*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } + ~any() { delete content_; } - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - T& get() { - return *this; - } + bool has_value() const { return content_ != nullptr; } - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - const T& get() const { - assert(content_); - auto p = dynamic_cast*>(content_); - assert(p); - if (!p) { - throw std::bad_cast(); - } - return p->value_; - } + template friend T &any_cast(any &val); - template < - typename T, - typename std::enable_if::value, std::nullptr_t>::type = nullptr - > - const any& get() const { - return *this; - } + template 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 - struct holder : placeholder { - holder(const T& value) : value_(value) {} - placeholder* clone() const override { - return new holder(value_); - } - T value_; - }; + template 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 T &any_cast(any &val) { + if (!val.content_) { throw std::bad_cast(); } + auto p = dynamic_cast *>(val.content_); + assert(p); + if (!p) { throw std::bad_cast(); } + return p->value_; +} + +template <> inline any &any_cast(any &val) { return val; } + +template const T &any_cast(const any &val) { + assert(val.content_); + auto p = dynamic_cast *>(val.content_); + assert(p); + if (!p) { throw std::bad_cast(); } + return p->value_; +} + +template <> inline const any &any_cast(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 -struct scope_exit -{ - explicit scope_exit(EF&& f) - : exit_function(std::move(f)) - , execute_on_destruction{true} {} +template 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 -auto make_scope_exit(EF&& exit_function) -> scope_exit { - return scope_exit::type>(std::forward(exit_function)); +auto make_scope_exit(EF &&exit_function) -> scope_exit { + return scope_exit::type>( + std::forward(exit_function)); } /*----------------------------------------------------------------------------- @@ -771,9 +743,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; } @@ -800,427 +770,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 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(ret * 16 + val); - i++; - } - return std::make_pair(ret, i); +inline std::pair 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(ret * 16 + val); + i++; + } + return std::make_pair(ret, i); } -inline std::pair 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(ret * 8 + val); +inline std::pair 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(ret * 8 + val); + i++; + } + return std::make_pair(ret, i); +} + +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 &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 dic_; +}; /*----------------------------------------------------------------------------- * PEG *---------------------------------------------------------------------------*/ /* -* Line information utility function -*/ -inline std::pair 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 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 -{ - // 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(*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); +} - // Definition name - const std::string& name() const { return name_; } +} // namespace udl - // Line number and column at which the matched string is - std::pair line_info() const { - return peg::line_info(ss, s_); - } +/* + * Semantic values + */ +struct SemanticValues : protected std::vector { + // Input text + const char *path = nullptr; + const char *ss = nullptr; + const std::vector *source_line_index = nullptr; - // Choice count - size_t choice_count() const { return choice_count_; } + // Matched string + const char *c_str() const { return s_; } + size_t length() const { return n_; } - // Choice number (0 based index) - size_t choice() const { return choice_; } + std::string str() const { return std::string(s_, n_); } - // Tokens - std::vector> tokens; + // Definition name + const std::string &name() const { return name_; } - 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_); - } + std::vector tags; - // Transform the semantic value vector to another vector - template - auto transform(size_t beg = 0, size_t end = static_cast(-1)) const -> vector { - return this->transform(beg, end, [](const any& v) { return v.get(); }); - } - - SemanticValues() : s_(nullptr), n_(0), choice_count_(0), choice_(0) {} - - using std::vector::iterator; - using std::vector::const_iterator; - using std::vector::size; - using std::vector::empty; - using std::vector::assign; - using std::vector::begin; - using std::vector::end; - using std::vector::rbegin; - using std::vector::rend; - using std::vector::operator[]; - using std::vector::at; - using std::vector::resize; - using std::vector::front; - using std::vector::back; - using std::vector::push_back; - using std::vector::pop_back; - using std::vector::insert; - using std::vector::erase; - using std::vector::clear; - using std::vector::swap; - using std::vector::emplace; - using std::vector::emplace_back; + // Line number and column at which the matched string is + std::pair line_info() const { + const auto &idx = *source_line_index; -private: - friend class Context; - friend class Sequence; - friend class PrioritizedChoice; - friend class Holder; + auto cur = static_cast(std::distance(ss, s_)); + auto it = std::lower_bound( + idx.begin(), idx.end(), cur, + [](size_t element, size_t value) { return element < value; }); - const char* s_; - size_t n_; - size_t choice_count_; - size_t choice_; - std::string name_; + auto id = static_cast(std::distance(idx.begin(), it)); + auto off = cur - (id == 0 ? 0 : idx[id - 1] + 1); + return std::make_pair(id + 1, off + 1); + } - template - auto transform(F f) const -> vector::type> { - vector::type> r; - for (const auto& v: *this) { - r.emplace_back(f(v)); - } - return r; - } + // Choice count + size_t choice_count() const { return choice_count_; } - template - auto transform(size_t beg, size_t end, F f) const -> vector::type> { - vector::type> r; - end = (std::min)(end, size()); - for (size_t i = beg; i < end; i++) { - r.emplace_back(f((*this)[i])); - } - return r; + // Choice number (0 based index) + size_t choice() const { return choice_; } + + // Tokens + std::vector> 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_); + } + + // Transform the semantic value vector to another vector + template + auto transform(size_t beg = 0, size_t end = static_cast(-1)) const + -> vector { + return this->transform(beg, end, + [](const any &v) { return any_cast(v); }); + } + + using std::vector::iterator; + using std::vector::const_iterator; + using std::vector::size; + using std::vector::empty; + using std::vector::assign; + using std::vector::begin; + using std::vector::end; + using std::vector::rbegin; + using std::vector::rend; + using std::vector::operator[]; + using std::vector::at; + using std::vector::resize; + using std::vector::front; + using std::vector::back; + using std::vector::push_back; + using std::vector::pop_back; + using std::vector::insert; + using std::vector::erase; + using std::vector::clear; + using std::vector::swap; + using std::vector::emplace; + using std::vector::emplace_back; - void reset() { - path = nullptr; - ss = nullptr; - tokens.clear(); +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 + auto transform(F f) const + -> vector::type> { + vector::type> r; + for (const auto &v : *this) { + r.emplace_back(f(v)); + } + return r; + } - s_ = nullptr; - n_ = 0; - choice_count_ = 0; - choice_ = 0; + template + auto transform(size_t beg, size_t end, F f) const + -> vector::type> { + vector::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::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - fn(std::forward(args)...); - return any(); -} - -template < - typename R, typename F, - typename std::enable_if::type, any>::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - return fn(std::forward(args)...); -} - -template < - typename R, typename F, - typename std::enable_if< - !std::is_void::value && - !std::is_same::type, any>::value, std::nullptr_t>::type = nullptr, - typename... Args> -any call(F fn, Args&&... args) { - return any(fn(std::forward(args)...)); -} - -class Action -{ +template ::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + fn(std::forward(args)...); + return any(); +} + +template ::type, any>::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + return fn(std::forward(args)...); +} + +template ::value && + !std::is_same::type, any>::value, + std::nullptr_t>::type = nullptr, + typename... Args> +any call(F fn, Args &&... args) { + return any(fn(std::forward(args)...)); +} + +class Action { public: - Action() = default; - - Action(const Action& rhs) : fn_(rhs.fn_) {} - - template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> - Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} - - template ::value, std::nullptr_t>::type = nullptr> - Action(F fn) : fn_(make_adaptor(fn, fn)) {} - - template ::value, std::nullptr_t>::type = nullptr> - Action(F /*fn*/) {} - - template ::value && !std::is_same::value, std::nullptr_t>::type = nullptr> - void operator=(F fn) { - fn_ = make_adaptor(fn, &F::operator()); - } + Action() = default; + Action(const Action &rhs) = default; + + template ::value && + !std::is_same::value, + std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, &F::operator())) {} + + template ::value, + std::nullptr_t>::type = nullptr> + Action(F fn) : fn_(make_adaptor(fn, fn)) {} + + template ::value, + std::nullptr_t>::type = nullptr> + Action(F /*fn*/) {} + + template ::value && + !std::is_same::value, + std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, &F::operator()); + } - template ::value, std::nullptr_t>::type = nullptr> - void operator=(F fn) { - fn_ = make_adaptor(fn, fn); - } + template ::value, + std::nullptr_t>::type = nullptr> + void operator=(F fn) { + fn_ = make_adaptor(fn, fn); + } - template ::value, std::nullptr_t>::type = nullptr> - void operator=(F /*fn*/) {} + template ::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 - struct TypeAdaptor_sv { - TypeAdaptor_sv(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call(fn_, sv); - } - std::function fn_; - }; - - template - struct TypeAdaptor_csv { - TypeAdaptor_csv(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& /*dt*/) { - return call(fn_, sv); - } - std::function fn_; - }; - - template - struct TypeAdaptor_sv_dt { - TypeAdaptor_sv_dt(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call(fn_, sv, dt); - } - std::function fn_; - }; - - template - struct TypeAdaptor_csv_dt { - TypeAdaptor_csv_dt(std::function fn) - : fn_(fn) {} - any operator()(SemanticValues& sv, any& dt) { - return call(fn_, sv, dt); - } - std::function fn_; - }; - - typedef std::function Fty; - - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv) const) { - return TypeAdaptor_sv(fn); - } + template struct TypeAdaptor_sv { + TypeAdaptor_sv(std::function fn) : fn_(fn) {} + any operator()(SemanticValues &sv, any & /*dt*/) { + return call(fn_, sv); + } + std::function fn_; + }; + + template struct TypeAdaptor_csv { + TypeAdaptor_csv(std::function fn) : fn_(fn) {} + any operator()(SemanticValues &sv, any & /*dt*/) { + return call(fn_, sv); + } + std::function fn_; + }; + + template struct TypeAdaptor_sv_dt { + TypeAdaptor_sv_dt(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues &sv, any &dt) { return call(fn_, sv, dt); } + std::function fn_; + }; + + template struct TypeAdaptor_csv_dt { + TypeAdaptor_csv_dt(std::function fn) + : fn_(fn) {} + any operator()(SemanticValues &sv, any &dt) { return call(fn_, sv, dt); } + std::function fn_; + }; + + typedef std::function Fty; + + template + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv) const) { + return TypeAdaptor_sv(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv) const) { - return TypeAdaptor_csv(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv) const) { + return TypeAdaptor_csv(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv)) { + return TypeAdaptor_sv(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv)) { + return TypeAdaptor_csv(fn); + } - template - Fty make_adaptor(F fn, R (* /*mf*/)(SemanticValues& sv)) { - return TypeAdaptor_sv(fn); - } + template + Fty make_adaptor(F fn, R (*)(SemanticValues &sv)) { + return TypeAdaptor_sv(fn); + } - template - Fty make_adaptor(F fn, R (* /*mf*/)(const SemanticValues& sv)) { - return TypeAdaptor_csv(fn); - } + template + Fty make_adaptor(F fn, R (*)(const SemanticValues &sv)) { + return TypeAdaptor_csv(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt) const) { - return TypeAdaptor_sv_dt(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv, any &dt) const) { + return TypeAdaptor_sv_dt(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt) const) { - return TypeAdaptor_csv_dt(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv, any &dt) const) { + return TypeAdaptor_csv_dt(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(SemanticValues &sv, any &dt)) { + return TypeAdaptor_sv_dt(fn); + } - template - Fty make_adaptor(F fn, R (F::* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt(fn); - } + template + Fty make_adaptor(F fn, R (F::*)(const SemanticValues &sv, any &dt)) { + return TypeAdaptor_csv_dt(fn); + } - template - Fty make_adaptor(F fn, R(* /*mf*/)(SemanticValues& sv, any& dt)) { - return TypeAdaptor_sv_dt(fn); - } + template + Fty make_adaptor(F fn, R (*)(SemanticValues &sv, any &dt)) { + return TypeAdaptor_sv_dt(fn); + } - template - Fty make_adaptor(F fn, R(* /*mf*/)(const SemanticValues& sv, any& dt)) { - return TypeAdaptor_csv_dt(fn); - } + template + Fty make_adaptor(F fn, R (*)(const SemanticValues &sv, any &dt)) { + return TypeAdaptor_csv_dt(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(-1); -} +inline bool success(size_t len) { return len != static_cast(-1); } -inline bool fail(size_t len) { - return len == static_cast(-1); -} +inline bool fail(size_t len) { return len == static_cast(-1); } /* * Context @@ -1229,2342 +1293,2833 @@ class Context; class Ope; class Definition; -typedef std::function Tracer; - -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> value_stack; - size_t value_stack_size; - std::vector>> args_stack; - - size_t nest_level; - - bool in_token; - - std::shared_ptr whitespaceOpe; - bool in_whitespace; - - std::shared_ptr wordOpe; - - std::vector> capture_scope_stack; - - const size_t def_count; - const bool enablePackratParsing; - std::vector cache_registered; - std::vector cache_success; - - std::map, std::tuple> cache_values; - - std::function tracer; - - Context( - const char* a_path, - const char* a_s, - size_t a_l, - size_t a_def_count, - std::shared_ptr a_whitespaceOpe, - std::shared_ptr 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); - } +typedef std::function + TracerEnter; - template - void packrat(const char* a_s, size_t def_id, size_t& len, any& val, T fn) { - if (!enablePackratParsing) { - fn(val); - return; - } +typedef std::function + TracerLeave; - auto col = a_s - s; - auto idx = def_count * static_cast(col) + def_id; +class Context { +public: + const char *path; + const char *s; + const size_t l; + std::vector source_line_index; - 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(-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; - } - } + const char *error_pos = nullptr; + const char *message_pos = nullptr; + std::string message; // TODO: should be `int`. - SemanticValues& push() { - assert(value_stack_size <= value_stack.size()); - if (value_stack_size == value_stack.size()) { - value_stack.emplace_back(std::make_shared()); - } - auto& sv = *value_stack[value_stack_size++]; - if (!sv.empty()) { - sv.clear(); - } - sv.reset(); - sv.path = path; - sv.ss = s; - return sv; - } + std::vector> value_stack; + size_t value_stack_size = 0; + std::vector>> args_stack; - void pop() { - value_stack_size--; - } + bool in_token = false; - void push_args(const std::vector>& args) { - args_stack.push_back(args); - } + std::shared_ptr whitespaceOpe; + bool in_whitespace = false; - void pop_args() { - args_stack.pop_back(); - } + std::shared_ptr wordOpe; - const std::vector>& top_args() const { - return args_stack[args_stack.size() - 1]; - } + std::vector> capture_scope_stack; + size_t capture_scope_stack_size = 0; - void push_capture_scope() { - capture_scope_stack.resize(capture_scope_stack.size() + 1); - } + const size_t def_count; + const bool enablePackratParsing; + std::vector cache_registered; + std::vector cache_success; - void pop_capture_scope() { - capture_scope_stack.pop_back(); - } + std::map, std::tuple> cache_values; - 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; - } - } + TracerEnter tracer_enter; + TracerLeave tracer_leave; - void set_error_pos(const char* a_s) { - if (error_pos < a_s) error_pos = a_s; - } + Context(const char *a_path, const char *a_s, size_t a_l, size_t a_def_count, + std::shared_ptr a_whitespaceOpe, std::shared_ptr 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 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); + for (size_t pos = 0; pos < l; pos++) { + if (s[pos] == '\n') { source_line_index.push_back(pos); } } -}; + source_line_index.push_back(l); -/* - * Parser operators - */ -class Ope -{ -public: - struct Visitor; + args_stack.resize(1); - 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; -}; + push_capture_scope(); + } -class Sequence : public Ope -{ -public: - Sequence(const Sequence& rhs) : opes_(rhs.opes_) {} + ~Context() { assert(!value_stack_size); } -#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 - Sequence(const Args& ...args) { - opes_ = std::vector>{ static_cast>(args)... }; - } -#else - template - Sequence(const Args& ...args) : opes_{ static_cast>(args)... } {} -#endif + Context(const Context &) = delete; + Context(Context &&) = delete; + Context operator=(const Context &) = delete; - Sequence(const std::vector>& opes) : opes_(opes) {} - Sequence(std::vector>&& 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(-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; + template + void packrat(const char *a_s, size_t def_id, size_t &len, any &val, T fn) { + if (!enablePackratParsing) { + fn(val); + return; } - void accept(Visitor& v) override; - - std::vector> opes_; -}; + auto col = a_s - s; + auto idx = def_count * static_cast(col) + def_id; -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 - PrioritizedChoice(const Args& ...args) { - opes_ = std::vector>{ static_cast>(args)... }; + 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(-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; } -#else - template - PrioritizedChoice(const Args& ...args) : opes_{ static_cast>(args)... } {} -#endif + } - PrioritizedChoice(const std::vector>& opes) : opes_(opes) {} - PrioritizedChoice(std::vector>&& 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++; - } - return static_cast(-1); + SemanticValues &push() { + assert(value_stack_size <= value_stack.size()); + if (value_stack_size == value_stack.size()) { + value_stack.emplace_back(std::make_shared()); + } 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> &&args) { + args_stack.emplace_back(args); + } + + void pop_args() { args_stack.pop_back(); } + + const std::vector> &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()); + } 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 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 accept(Visitor& v) override; + 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; - size_t size() const { return opes_.size(); } + mutable size_t next_trace_id = 0; + mutable std::list trace_ids; +}; - std::vector> opes_; +/* + * Parser operators + */ +class Ope { +public: + 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 ZeroOrMore : public Ope -{ +class Sequence : public Ope { public: - ZeroOrMore(const std::shared_ptr& 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(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; - break; - } - i += len; - } - return i; + template + Sequence(const Args &... args) + : opes_{static_cast>(args)...} {} + Sequence(const std::vector> &opes) : opes_(opes) {} + Sequence(std::vector> &&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(-1); } + i += len; + } + if (!chldsv.empty()) { + for (size_t j = 0; j < chldsv.size(); j++) { + sv.emplace_back(std::move(chldsv[j])); + } } + 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::shared_ptr ope_; + std::vector> opes_; }; -class OneOrMore : public Ope -{ +class PrioritizedChoice : public Ope { public: - OneOrMore(const std::shared_ptr& ope) : ope_(ope) {} - - 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(-1); - } + template + PrioritizedChoice(const Args &... args) + : opes_{static_cast>(args)...} {} + PrioritizedChoice(const std::vector> &opes) + : opes_(opes) {} + PrioritizedChoice(std::vector> &&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])); + } } - 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(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; - break; - } - i += len; + 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])); + } } - return i; + + c.shift_capture_values(); + return len; + } + id++; } + return static_cast(-1); + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr ope_; + size_t size() const { return opes_.size(); } + + std::vector> opes_; }; -class Option : public Ope -{ +class Repetition : public Ope { public: - Option(const std::shared_ptr& 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(save_sv_size)); - } - if (sv.tokens.size() != save_tok_size) { - sv.tokens.erase(sv.tokens.begin() + static_cast(save_tok_size)); - } - c.error_pos = save_error_pos; - return 0; - } + Repetition(const std::shared_ptr &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(-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(save_sv_size)); + sv.tags.erase(sv.tags.begin() + + static_cast(save_sv_size)); + } + if (sv.tokens.size() != save_tok_size) { + sv.tokens.erase(sv.tokens.begin() + + static_cast(save_tok_size)); + } + c.error_pos = save_error_pos; + break; + } + i += len; + count++; } + return i; + } + + void accept(Visitor &v) override; + + bool is_zom() const { + return min_ == 0 && max_ == std::numeric_limits::max(); + } + + static std::shared_ptr zom(const std::shared_ptr &ope) { + return std::make_shared(ope, 0, + std::numeric_limits::max()); + } + + static std::shared_ptr oom(const std::shared_ptr &ope) { + return std::make_shared(ope, 1, + std::numeric_limits::max()); + } - void accept(Visitor& v) override; + static std::shared_ptr opt(const std::shared_ptr &ope) { + return std::make_shared(ope, 0, 1); + } - std::shared_ptr ope_; + std::shared_ptr ope_; + size_t min_; + size_t max_; }; -class AndPredicate : public Ope -{ +class AndPredicate : public Ope { public: - AndPredicate(const std::shared_ptr& 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(-1); - } + AndPredicate(const std::shared_ptr &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(-1); } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr ope_; + std::shared_ptr ope_; }; -class NotPredicate : public Ope -{ +class NotPredicate : public Ope { public: - NotPredicate(const std::shared_ptr& 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(-1); - } else { - c.error_pos = save_error_pos; - return 0; - } + NotPredicate(const std::shared_ptr &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(-1); + } else { + c.error_pos = save_error_pos; + return 0; } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::shared_ptr ope_; + std::shared_ptr ope_; }; -class LiteralString : public Ope - , public std::enable_shared_from_this -{ +class Dictionary : public Ope, public std::enable_shared_from_this { public: - LiteralString(const std::string& s) - : lit_(s) - , init_is_word_(false) - , is_word_(false) - {} + Dictionary(const std::vector &v) : trie_(v) {} - 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_; + Trie trie_; }; -class CharacterClass : public Ope - , public std::enable_shared_from_this -{ +class LiteralString : public Ope, + public std::enable_shared_from_this { 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; - } - } - } + LiteralString(const std::string &s, bool ignore_case) + : lit_(s), ignore_case_(ignore_case), init_is_word_(false), + is_word_(false) {} - CharacterClass(const std::vector>& ranges) : ranges_(ranges) {} + size_t parse_core(const char *s, size_t n, SemanticValues &sv, Context &c, + any &dt) const override; - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override { - c.trace("CharacterClass", s, n, sv, dt); + void accept(Visitor &v) override; - if (n < 1) { - c.set_error_pos(s); - return static_cast(-1); - } + std::string lit_; + bool ignore_case_; + mutable bool init_is_word_; + mutable bool is_word_; +}; - char32_t cp = 0; - auto len = decode_codepoint(s, n, cp); +class CharacterClass : public Ope, + public std::enable_shared_from_this { +public: + 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()); + } - if (!ranges_.empty()) { - for (const auto& range: ranges_) { - if (range.first <= cp && cp <= range.second) { - return len; - } - } + CharacterClass(const std::vector> &ranges, + bool negated) + : ranges_(ranges), negated_(negated) { + assert(!ranges_.empty()); + } + + 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(-1); + } + + char32_t cp = 0; + auto len = decode_codepoint(s, n, cp); + + for (const auto &range : ranges_) { + if (range.first <= cp && cp <= range.second) { + if (negated_) { + c.set_error_pos(s); + return static_cast(-1); + } else { + return len; } + } + } - c.set_error_pos(s); - return static_cast(-1); + if (negated_) { + return len; + } else { + c.set_error_pos(s); + return static_cast(-1); } + } - void accept(Visitor& v) override; + void accept(Visitor &v) override; - std::vector> ranges_; + std::vector> ranges_; + bool negated_; }; -class Character : public Ope - , public std::enable_shared_from_this -{ +class Character : public Ope, public std::enable_shared_from_this { 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(-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(-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 -{ +class AnyCharacter : public Ope, + public std::enable_shared_from_this { 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(-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(-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) {} + CaptureScope(const std::shared_ptr &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_; + std::shared_ptr ope_; }; -class Capture : public Ope -{ +class Capture : public Ope { public: - typedef std::function MatchAction; + typedef std::function MatchAction; - Capture(const std::shared_ptr& ope, MatchAction ma) - : ope_(ope), match_action_(ma) {} + Capture(const std::shared_ptr &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_; - MatchAction match_action_; + std::shared_ptr ope_; + MatchAction match_action_; }; -class TokenBoundary : public Ope -{ +class TokenBoundary : public Ope { public: - TokenBoundary(const std::shared_ptr& ope) : ope_(ope) {} + TokenBoundary(const std::shared_ptr &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_; + std::shared_ptr ope_; }; -class Ignore : public Ope -{ +class Ignore : public Ope { public: - Ignore(const std::shared_ptr& 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) {} + + 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_; + std::shared_ptr ope_; }; -typedef std::function Parser; +typedef std::function + 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 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 + fn_; }; -class WeakHolder : public Ope -{ +class WeakHolder : public Ope { public: - WeakHolder(const std::shared_ptr& 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) : 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 weak_; + std::weak_ptr 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_; - Definition* outer_; + const char *trace_name() const; - friend class Definition; + std::shared_ptr ope_; + Definition *outer_; + mutable std::string trace_name_; + + friend class Definition; }; typedef std::unordered_map Grammar; -class Reference : public Ope - , public std::enable_shared_from_this -{ +class Reference : public Ope, public std::enable_shared_from_this { public: - Reference( - const Grammar& grammar, - const std::string& name, - const char* s, - bool is_macro, - const std::vector>& 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 get_core_operator() const; - - const Grammar& grammar_; - const std::string name_; - const char* s_; - - const bool is_macro_; - const std::vector> args_; - - Definition* rule_; - size_t iarg_; + Reference(const Grammar &grammar, const std::string &name, const char *s, + bool is_macro, const std::vector> &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 get_core_operator() const; + + const Grammar &grammar_; + const std::string name_; + const char *s_; + + const bool is_macro_; + const std::vector> args_; + + Definition *rule_; + size_t iarg_; }; -class Whitespace : public Ope -{ +class Whitespace : public Ope { public: - Whitespace(const std::shared_ptr& ope) : ope_(ope) {} + Whitespace(const std::shared_ptr &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); + } - 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); - } + void accept(Visitor &v) override; + + std::shared_ptr ope_; +}; + +class BackReference : public Ope { +public: + BackReference(const std::string &name) : name_(name) {} - 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_; + void accept(Visitor &v) override; + + std::string name_; }; -class BackReference : public Ope -{ +class PrecedenceClimbing : public Ope { public: - BackReference(const std::string& name) : name_(name) {} + using BinOpeInfo = std::map>; + + PrecedenceClimbing(const std::shared_ptr &atom, + const std::shared_ptr &binop, const BinOpeInfo &info, + const Definition &rule) + : atom_(atom), binop_(binop), info_(info), rule_(rule) {} + + 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; - size_t parse(const char* s, size_t n, SemanticValues& sv, Context& c, any& dt) const override; + std::shared_ptr atom_; + std::shared_ptr binop_; + BinOpeInfo info_; + const Definition &rule_; - void accept(Visitor& v) override; +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 -std::shared_ptr seq(Args&& ...args) { - return std::make_shared(static_cast>(args)...); +template std::shared_ptr seq(Args &&... args) { + return std::make_shared(static_cast>(args)...); +} + +template std::shared_ptr cho(Args &&... args) { + return std::make_shared( + static_cast>(args)...); +} + +inline std::shared_ptr zom(const std::shared_ptr &ope) { + return Repetition::zom(ope); +} + +inline std::shared_ptr oom(const std::shared_ptr &ope) { + return Repetition::oom(ope); +} + +inline std::shared_ptr opt(const std::shared_ptr &ope) { + return Repetition::opt(ope); +} + +inline std::shared_ptr rep(const std::shared_ptr &ope, size_t min, + size_t max) { + return std::make_shared(ope, min, max); } -template -std::shared_ptr cho(Args&& ...args) { - return std::make_shared(static_cast>(args)...); +inline std::shared_ptr apd(const std::shared_ptr &ope) { + return std::make_shared(ope); } -inline std::shared_ptr zom(const std::shared_ptr& ope) { - return std::make_shared(ope); +inline std::shared_ptr npd(const std::shared_ptr &ope) { + return std::make_shared(ope); } -inline std::shared_ptr oom(const std::shared_ptr& ope) { - return std::make_shared(ope); +inline std::shared_ptr dic(const std::vector &v) { + return std::make_shared(v); } -inline std::shared_ptr opt(const std::shared_ptr& ope) { - return std::make_shared