[
  {
    "path": ".clang-format",
    "content": "---\n  BasedOnStyle: Google\n  AccessModifierOffset: '-2'\n  AlignTrailingComments: 'true'\n  AllowAllParametersOfDeclarationOnNextLine: 'false'\n  AlwaysBreakTemplateDeclarations: 'No'\n  BreakBeforeBraces: Attach\n  ColumnLimit: '100'\n  ConstructorInitializerAllOnOneLineOrOnePerLine: 'true'\n  IncludeBlocks: Regroup\n  IndentPPDirectives: AfterHash\n  IndentWidth: '2'\n  NamespaceIndentation: All\n  BreakBeforeBinaryOperators: All\n  BreakBeforeTernaryOperators: 'true'\n...\n"
  },
  {
    "path": ".cmake-format",
    "content": "format:\n  tab_size: 2\n  line_width: 100\n  dangle_parens: true\n\nparse:\n  additional_commands:\n    cpmaddpackage:\n      pargs:\n        nargs: '*'\n        flags: []\n      spelling: CPMAddPackage\n      kwargs: &cpmaddpackagekwargs\n        NAME: 1\n        FORCE: 1\n        VERSION: 1\n        GIT_TAG: 1\n        DOWNLOAD_ONLY: 1\n        GITHUB_REPOSITORY: 1\n        GITLAB_REPOSITORY: 1\n        GIT_REPOSITORY: 1\n        SVN_REPOSITORY: 1\n        SVN_REVISION: 1\n        SOURCE_DIR: 1\n        DOWNLOAD_COMMAND: 1\n        FIND_PACKAGE_ARGUMENTS: 1\n        NO_CACHE: 1\n        GIT_SHALLOW: 1\n        URL: 1\n        URL_HASH: 1\n        URL_MD5: 1\n        DOWNLOAD_NAME: 1\n        DOWNLOAD_NO_EXTRACT: 1\n        HTTP_USERNAME: 1\n        HTTP_PASSWORD: 1\n        OPTIONS: +\n    cpmfindpackage:\n      pargs:\n        nargs: '*'\n        flags: []\n      spelling: CPMFindPackage\n      kwargs: *cpmaddpackagekwargs\n    packageproject:\n      pargs:\n        nargs: '*'\n        flags: []\n      spelling: packageProject\n      kwargs:\n        NAME: 1\n        VERSION: 1\n        NAMESPACE: 1\n        INCLUDE_DIR: 1\n        INCLUDE_DESTINATION: 1\n        BINARY_DIR: 1\n        COMPATIBILITY: 1\n        VERSION_HEADER: 1\n        DEPENDENCIES: +\n"
  },
  {
    "path": ".github/workflows/examples.yml",
    "content": "name: Examples\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: configure\n        run: cmake -Sexample -Bbuild\n\n      - name: build\n        run: cmake --build build -j4\n"
  },
  {
    "path": ".github/workflows/install.yml",
    "content": "name: Install\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: build and install library\n        run: |\n          CXX=g++-9 cmake -Sglue -Bbuild -DCMAKE_BUILD_TYPE=Release\n          sudo cmake --build build --target install\n          rm -rf build\n\n      - name: configure\n        run: CXX=g++-9 cmake -Stest -Bbuild -DTEST_INSTALLED_VERSION=1\n\n      - name: build\n        run: cmake --build build --config Debug -j4\n\n      - name: test\n        run: |\n          cd build\n          ctest --build-config Debug\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: MacOS\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: macos-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: configure\n        run: cmake -Stest -Bbuild\n\n      - name: build\n        run: cmake --build build --config Debug -j4\n\n      - name: test\n        run: |\n          cd build\n          ctest --build-config Debug\n"
  },
  {
    "path": ".github/workflows/style.yml",
    "content": "name: Style\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: macos-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: Install format dependencies\n        run: |\n          brew install clang-format\n          pip3 install cmake_format==0.6.11 pyyaml\n\n      - name: configure\n        run: cmake -Stest -Bbuild\n\n      - name: check style\n        run: cmake --build build --target check-format\n"
  },
  {
    "path": ".github/workflows/ubuntu.yml",
    "content": "name: Ubuntu\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: install valgrind\n        run: |\n          sudo apt-get update\n          sudo apt install -y valgrind\n\n      - name: configure\n        run: CXX=g++-8 cmake -Stest -Bbuild -DCMAKE_BUILD_TYPE=Debug\n\n      - name: build\n        run: cmake --build build -j4\n\n      - name: test\n        run: cmake --build build -j4\n\n      - name: run tests with valgrind\n        run: valgrind --track-origins=yes --error-exitcode=1 --leak-check=full ./build/PEGParserTests\n\n      - name: configure with code coverage\n        run: CXX=g++-8 cmake -Stest -Bbuild -DENABLE_TEST_COVERAGE=1\n\n      - name: build with code coverage\n        run: cmake --build build -j4\n\n      - name: test with code coverage\n        run: |\n          cd build\n          ctest --build-config Debug\n\n      - name: install code coverage tools\n        run: |\n          wget https://github.com/linux-test-project/lcov/releases/download/v1.14/lcov-1.14.tar.gz\n          tar xvfz lcov-1.14.tar.gz;\n          sudo make install -C lcov-1.14\n\n      - name: collect code coverage\n        run: |\n          lcov --gcov-tool $(which gcov-8) --directory . --capture --no-external --exclude \"*tests*\" --exclude \"*_deps*\" --quiet --output-file coverage.info\n          lcov --gcov-tool $(which gcov-8) --list coverage.info\n          bash <(curl -s https://codecov.io/bash) -f coverage.info || echo \"Codecov did not collect coverage reports\"\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows\n\non:\n  push:\n    branches:\n      - master\n  pull_request:\n    branches:\n      - master\n\nenv:\n  CTEST_OUTPUT_ON_FAILURE: 1\n  CPM_SOURCE_CACHE: ${{ github.workspace }}/cpm_modules\n\njobs:\n  build:\n    runs-on: windows-latest\n\n    steps:\n      - uses: actions/checkout@v2\n\n      - uses: actions/cache@v2\n        with:\n          path: \"**/cpm_modules\"\n          key: ${{ github.workflow }}-cpm-modules-${{ hashFiles('**/CMakeLists.txt', '**/*.cmake') }}\n\n      - name: configure\n        run: cmake -Stest -Bbuild\n\n      - name: build\n        run: cmake --build build --config Debug -j4\n\n      - name: test\n        run: |\n          cd build\n          ctest --build-config Debug\n"
  },
  {
    "path": ".gitignore",
    "content": "build*\n.vscode\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14 FATAL_ERROR)\n\n# ---- Project ----\n\nproject(\n  PEGParser\n  VERSION 2.1\n  LANGUAGES CXX\n)\n\n# ---- Include guards ----\n\nif(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)\n  message(\n    FATAL_ERROR\n      \"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\"\n  )\nendif()\n\n# ---- Add dependencies via CPM ----\n\ninclude(cmake/CPM.cmake)\n\n# PackageProject.cmake will be used to make our target installable\nCPMAddPackage(\n  NAME PackageProject.cmake\n  GITHUB_REPOSITORY TheLartians/PackageProject.cmake\n  VERSION 1.4\n)\n\nCPMAddPackage(\n  NAME EasyIterator\n  GITHUB_REPOSITORY TheLartians/EasyIterator\n  VERSION 1.4\n)\n\n# ---- Add source files ----\n\nfile(GLOB_RECURSE headers CONFIGURE_DEPENDS \"${CMAKE_CURRENT_SOURCE_DIR}/include/*.h\")\nfile(GLOB_RECURSE sources CONFIGURE_DEPENDS \"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp\")\n\n# ---- Create library ----\n\nadd_library(PEGParser ${headers} ${sources})\n\nset_target_properties(PEGParser PROPERTIES CXX_STANDARD 17)\n\ntarget_compile_options(PEGParser PUBLIC \"$<$<BOOL:${MSVC}>:/permissive->\")\n\ntarget_link_libraries(PEGParser PRIVATE EasyIterator)\n\ntarget_include_directories(\n  PEGParser PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>\n                   $<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>\n)\n\n# ---- Create an installable target ----\n# this allows users to install and find the library via `find_package()`.\n\npackageProject(\n  NAME ${PROJECT_NAME}\n  NAMESPACE ${PROJECT_NAME}\n  VERSION ${PROJECT_VERSION}\n  BINARY_DIR ${PROJECT_BINARY_DIR}\n  INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include\n  INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION}\n  DEPENDENCIES EasyIterator\n)\n"
  },
  {
    "path": "LICENSE",
    "content": "BSD 3-Clause License\n\nCopyright (c) 2018, Lars Melchior\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are met:\n\n* Redistributions of source code must retain the above copyright notice, this\n  list of conditions and the following disclaimer.\n\n* Redistributions in binary form must reproduce the above copyright notice,\n  this list of conditions and the following disclaimer in the documentation\n  and/or other materials provided with the distribution.\n\n* Neither the name of the copyright holder nor the names of its\n  contributors may be used to endorse or promote products derived from\n  this software without specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\"\nAND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE\nIMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE\nFOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL\nDAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\nCAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,\nOR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE\nOF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n"
  },
  {
    "path": "README.md",
    "content": "[![Actions Status](https://github.com/TheLartians/PEGParser/workflows/MacOS/badge.svg)](https://github.com/TheLartians/PEGParser/actions)\n[![Actions Status](https://github.com/TheLartians/PEGParser/workflows/Windows/badge.svg)](https://github.com/TheLartians/PEGParser/actions)\n[![Actions Status](https://github.com/TheLartians/PEGParser/workflows/Ubuntu/badge.svg)](https://github.com/TheLartians/PEGParser/actions)\n[![Actions Status](https://github.com/TheLartians/PEGParser/workflows/Style/badge.svg)](https://github.com/TheLartians/PEGParser/actions)\n[![codecov](https://codecov.io/gh/TheLartians/PEGParser/branch/master/graph/badge.svg)](https://codecov.io/gh/TheLartians/PEGParser)\n\n# PEGParser\n\nA linear-time C++17 PEG parser generator supporting memoization, left-recursion and context-dependent grammars.\n\n## Example\n\nThe following defines a simple calculator program. It is able to parse and evaluate the basic operations `+`, `-`, `*`, `/` while obeying operator and bracket precedence and ignoring whitespace characters between tokens.\n\n```c++\n#include <peg_parser/generator.h>\n#include <iostream>\n\nvoid example() {\n  peg_parser::ParserGenerator<float> g;\n\n  // Define grammar and evaluation rules\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n  g[\"Sum\"     ] << \"Add | Subtract | Product\";\n  g[\"Product\" ] << \"Multiply | Divide | Atomic\";\n  g[\"Atomic\"  ] << \"Number | '(' Sum ')'\";\n  g[\"Add\"     ] << \"Sum '+' Product\"    >> [](auto e){ return e[0].evaluate() + e[1].evaluate(); };\n  g[\"Subtract\"] << \"Sum '-' Product\"    >> [](auto e){ return e[0].evaluate() - e[1].evaluate(); };\n  g[\"Multiply\"] << \"Product '*' Atomic\" >> [](auto e){ return e[0].evaluate() * e[1].evaluate(); };\n  g[\"Divide\"  ] << \"Product '/' Atomic\" >> [](auto e){ return e[0].evaluate() / e[1].evaluate(); };\n  g[\"Number\"  ] << \"'-'? [0-9]+ ('.' [0-9]+)?\" >> [](auto e){ return stof(e.string()); };\n  g.setStart(g[\"Sum\"]);\n\n  // Execute a string\n  auto input = \"1 + 2 * (3+4)/2 - 3\";\n  float result = g.run(input); // -> 5\n  std::cout << input << \" = \" << result << std::endl;\n}\n```\n\n## Quickstart\n\nPEGParser requires at least cmake 3.14 and the ability to compile C++17 code. The following shows how to compile and run the calculator example.\n\n```bash\ngit clone https://github.com/TheLartians/PegParser\ncd PegParser\ncmake -Sexample -Bbuild/example\ncmake --build build/example -j8\n./build/example/calculator\n```\n\nYou should familiarize yourself with the syntax of [parsing expression grammars](http://en.wikipedia.org/wiki/Parsing_expression_grammar). The included [examples](example) should help you to get started.\n\n## Installation and usage\n\nPEGParser can be easily added to your project through [CPM.cmake](https://github.com/TheLartians/CPM.cmake).\n\n```cmake\nCPMAddPackage(\n  NAME PEGParser\n  VERSION 2.1.1\n  GITHUB_REPOSITORY TheLartians/PEGParser\n)\n\ntarget_link_libraries(myProject PEGParser::PEGParser)\n```\n\n## Project goals\n\nPEGParser is designed for ease-of-use and rapid prototyping of grammars with arbitrary complexity, and builds its parsers at run time.\nSo far no work has been invested on optimizing the library, however it runs fast enough to be used in several production projects.\n\n## Time complexity\n\nPEGParser uses memoization, resulting in linear time complexity (as a function of input string length) for grammars without left-recursion.\nLeft-recursive grammars have squared time complexity in worst case.\nMemoization can also be disabled on a per-rule basis, reducing the memory footprint and allowing context-dependent rules.\n"
  },
  {
    "path": "cmake/CPM.cmake",
    "content": "set(CPM_DOWNLOAD_VERSION 0.28.0)\n\nif(CPM_SOURCE_CACHE)\n  set(CPM_DOWNLOAD_LOCATION \"${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake\")\nelseif(DEFINED ENV{CPM_SOURCE_CACHE})\n  set(CPM_DOWNLOAD_LOCATION \"$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake\")\nelse()\n  set(CPM_DOWNLOAD_LOCATION \"${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake\")\nendif()\n\nif(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))\n  message(STATUS \"Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}\")\n  file(DOWNLOAD\n       https://github.com/TheLartians/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake\n       ${CPM_DOWNLOAD_LOCATION}\n  )\nendif()\n\ninclude(${CPM_DOWNLOAD_LOCATION})\n"
  },
  {
    "path": "cmake/tools.cmake",
    "content": "# this file contains a list of tools that can be activated and downloaded on-demand each tool is\n# enabled during configuration by passing an additional `-DUSE_<TOOL>=<VALUE>` argument to CMake\n\n# only activate tools for top level project\nif(NOT PROJECT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)\n  return()\nendif()\n\ninclude(${CMAKE_CURRENT_LIST_DIR}/CPM.cmake)\n\n# enables sanitizers support using the the `USE_SANITIZER` flag available values are: Address,\n# Memory, MemoryWithOrigins, Undefined, Thread, Leak, 'Address;Undefined'\nif(USE_SANITIZER OR USE_STATIC_ANALYZER)\n  CPMAddPackage(\n    NAME StableCoder-cmake-scripts\n    GITHUB_REPOSITORY StableCoder/cmake-scripts\n    GIT_TAG 3d2d5a9fb26f0ce24e3e4eaeeff686ec2ecfb3fb\n  )\n\n  if(USE_SANITIZER)\n    include(${StableCoder-cmake-scripts_SOURCE_DIR}/sanitizers.cmake)\n  endif()\n\n  if(USE_STATIC_ANALYZER)\n    if(\"clang-tidy\" IN_LIST USE_STATIC_ANALYZER)\n      set(CLANG_TIDY\n          ON\n          CACHE INTERNAL \"\"\n      )\n    else()\n      set(CLANG_TIDY\n          OFF\n          CACHE INTERNAL \"\"\n      )\n    endif()\n    if(\"iwyu\" IN_LIST USE_STATIC_ANALYZER)\n      set(IWYU\n          ON\n          CACHE INTERNAL \"\"\n      )\n    else()\n      set(IWYU\n          OFF\n          CACHE INTERNAL \"\"\n      )\n    endif()\n    if(\"cppcheck\" IN_LIST USE_STATIC_ANALYZER)\n      set(CPPCHECK\n          ON\n          CACHE INTERNAL \"\"\n      )\n    else()\n      set(CPPCHECK\n          OFF\n          CACHE INTERNAL \"\"\n      )\n    endif()\n\n    include(${StableCoder-cmake-scripts_SOURCE_DIR}/tools.cmake)\n\n    clang_tidy(${CLANG_TIDY_ARGS})\n    include_what_you_use(${IWYU_ARGS})\n    cppcheck(${CPPCHECK_ARGS})\n  endif()\nendif()\n\n# enables CCACHE support through the USE_CCACHE flag possible values are: YES, NO or equivalent\nif(USE_CCACHE)\n  CPMAddPackage(\n    NAME Ccache.cmake\n    GITHUB_REPOSITORY TheLartians/Ccache.cmake\n    VERSION 1.2.1\n  )\nendif()\n"
  },
  {
    "path": "codecov.yaml",
    "content": "ignore:\n  - \"test\"\n\ncomment:\n  require_changes: true"
  },
  {
    "path": "example/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5 FATAL_ERROR)\n\n# ---- Project ----\n\nproject(PEGParserExamples CXX)\n\n# --- Import tools ----\n\ninclude(../cmake/tools.cmake)\n\n# ---- Add dependencies ----\n\ninclude(../cmake/CPM.cmake)\n\nCPMAddPackage(NAME PEGParser SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)\n\n# ---- Create binaries ----\n\nfile(GLOB example_sources ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp)\n\nforeach(example_source_file ${example_sources})\n  get_filename_component(filename ${example_source_file} NAME)\n  string(REPLACE \".cpp\" \"\" example_name ${filename})\n  add_executable(${example_name} ${example_source_file})\n  set_target_properties(${example_name} PROPERTIES CXX_STANDARD 17)\n  target_link_libraries(${example_name} PEGParser::PEGParser)\nendforeach()\n"
  },
  {
    "path": "example/calculator.cpp",
    "content": "/**\n *  This example demonstrate how we can use peg_parser::parser to define a\n * simple command-line calculator. The parser supports the basic operators `+`,\n * `-`, `*`, `/`, `^` as well as using and assigning variables via `=`.\n *\n *  Note, that The grammar is defined in a left-recursive way. While this is\n * easiest to implement, it recomended to rewrite left-recursive grammars\n * sequentially for optimal performance.\n */\n\n#include <peg_parser/generator.h>\n\n#include <cmath>\n#include <iostream>\n#include <unordered_map>\n\nint main() {\n  using namespace std;\n  using VariableMap = unordered_map<string, float>;\n\n  peg_parser::ParserGenerator<float, VariableMap &> calculator;\n\n  auto &g = calculator;\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n\n  g[\"Expression\"] << \"Set | Sum\";\n  g[\"Set\"] << \"Name '=' Sum\" >> [](auto e, auto &v) { return v[e[0].string()] = e[1].evaluate(v); };\n  g[\"Sum\"] << \"Add | Subtract | Product\";\n  g[\"Product\"] << \"Multiply | Divide | Exponent\";\n  g[\"Exponent\"] << \"Power | Atomic\";\n  g[\"Atomic\"] << \"Number | Brackets | Variable\";\n  g[\"Brackets\"] << \"'(' Sum ')'\";\n  g[\"Add\"] << \"Sum '+' Product\" >>\n      [](auto e, auto &v) { return e[0].evaluate(v) + e[1].evaluate(v); };\n  g[\"Subtract\"] << \"Sum '-' Product\" >>\n      [](auto e, auto &v) { return e[0].evaluate(v) - e[1].evaluate(v); };\n  g[\"Multiply\"] << \"Product '*' Exponent\" >>\n      [](auto e, auto &v) { return e[0].evaluate(v) * e[1].evaluate(v); };\n  g[\"Divide\"] << \"Product '/' Exponent\" >>\n      [](auto e, auto &v) { return e[0].evaluate(v) / e[1].evaluate(v); };\n  g[\"Power\"] << \"Atomic ('^' Exponent)\" >>\n      [](auto e, auto &v) { return pow(e[0].evaluate(v), e[1].evaluate(v)); };\n  g[\"Variable\"] << \"Name\" >> [](auto e, auto &v) { return v[e[0].string()]; };\n  g[\"Name\"] << \"[a-zA-Z] [a-zA-Z0-9]*\";\n  g[\"Number\"] << \"'-'? [0-9]+ ('.' [0-9]+)?\" >> [](auto e, auto &) { return stod(e.string()); };\n\n  g.setStart(g[\"Expression\"]);\n\n  cout << \"Enter an expression to be evaluated.\\n\";\n\n  VariableMap variables;\n\n  while (true) {\n    string str;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    try {\n      auto result = calculator.run(str, variables);\n      cout << str << \" = \" << result << endl;\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << string(syntax->begin, ' ');\n      cout << string(syntax->length(), '~');\n      cout << \"^\\n\";\n      cout << \"  \"\n           << \"Syntax error while parsing \" << syntax->rule->name << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "example/calculator_sequental.cpp",
    "content": "/**\n *  A command-line calculator that uses a sequental grammar instead of\n * left-recursion.\n */\n\n#include <peg_parser/generator.h>\n\n#include <cmath>\n#include <iostream>\n#include <numeric>\n#include <unordered_map>\n\nint main() {\n  using namespace std;\n  using VariableMap = unordered_map<string, float>;\n\n  peg_parser::ParserGenerator<float, VariableMap &> calculator;\n\n  auto &g = calculator;\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n\n  g[\"Expression\"] << \"Assign | Sum\";\n\n  g[\"Assign\"] << \"Name '=' Sum\" >>\n      [](auto e, auto &v) { return v[e[0].string()] = e[1].evaluate(v); };\n\n  g[\"Sum\"] << \"Product Summand*\" >> [=](auto e, auto &v) {\n    return std::accumulate(e.begin(), e.end(), 0,\n                           [&](auto a, auto b) { return a + b.evaluate(v); });\n  };\n  g[\"PositiveSummand\"] << \"'+' Product\" >> [=](auto e, auto &v) { return e[0].evaluate(v); };\n  g[\"NegativeSummand\"] << \"'-' Product\" >> [=](auto e, auto &v) { return -e[0].evaluate(v); };\n  g[\"Summand\"] << \"PositiveSummand | NegativeSummand\";\n\n  g[\"Product\"] << \"Power Term*\" >> [](auto e, auto &v) {\n    return std::accumulate(e.begin(), e.end(), 1,\n                           [&](auto a, auto b) { return a * b.evaluate(v); });\n  };\n  g[\"NormalTerm\"] << \"'*' Power\" >> [=](auto e, auto &v) { return e[0].evaluate(v); };\n  g[\"InverseTerm\"] << \"'/' Power\" >> [=](auto e, auto &v) { return 1 / e[0].evaluate(v); };\n  g[\"Term\"] << \"NormalTerm | InverseTerm\";\n\n  g[\"Power\"] << \"Atomic ('^' Power) | Atomic\" >> [](auto e, auto &v) {\n    return e.size() == 2 ? pow(e[0].evaluate(v), e[1].evaluate(v)) : e[0].evaluate(v);\n  };\n\n  g[\"Atomic\"] << \"Number | Brackets | Variable\";\n\n  g[\"Brackets\"] << \"'(' Sum ')'\";\n  g[\"Variable\"] << \"Name\" >> [](auto e, auto &v) { return v[e[0].string()]; };\n  g[\"Name\"] << \"[a-zA-Z] [a-zA-Z0-9]*\";\n\n  // We can also use other programs as rules\n  g.setProgramRule(\"Number\", peg_parser::presets::createFloatProgram());\n\n  g.setStart(g[\"Expression\"]);\n\n  cout << \"Enter an expression to be evaluated.\\n\";\n\n  VariableMap variables;\n\n  while (true) {\n    string str;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    try {\n      auto result = calculator.run(str, variables);\n      cout << str << \" = \" << result << endl;\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << string(syntax->begin, ' ');\n      cout << string(syntax->length(), '~');\n      cout << \"^\\n\";\n      cout << \"  \"\n           << \"Syntax error while parsing \" << syntax->rule->name << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "example/calculator_visitor.cpp",
    "content": "/**\n *  This example demonstrate how we can use peg_parser::parser to define a\n * command-line calculator and use a visitor pattern to evaluate the result.\n */\n\n#include <peg_parser/generator.h>\n\n#include <cmath>\n#include <iostream>\n#include <unordered_map>\n\nint main() {\n  using namespace std;\n\n  struct Visitor;\n\n  using Expression = peg_parser::Interpreter<void, Visitor &>::Expression;\n\n  struct Visitor {\n    float result;\n    unordered_map<string, float> variables;\n\n    float getValue(Expression e) {\n      e.evaluate(*this);\n      return result;\n    }\n\n    void visitAddition(Expression l, Expression r) { result = getValue(l) + getValue(r); }\n    void visitSubtraction(Expression l, Expression r) { result = getValue(l) - getValue(r); }\n    void visitMultiplication(Expression l, Expression r) { result = getValue(l) * getValue(r); }\n    void visitDivision(Expression l, Expression r) { result = getValue(l) / getValue(r); }\n    void visitPower(Expression l, Expression r) { result = pow(getValue(l), getValue(r)); }\n    void visitVariable(Expression name) { result = variables[name.string()]; }\n    void visitAssignment(Expression name, Expression value) {\n      result = (variables[name.string()] = getValue(value));\n    }\n    void visitNumber(Expression value) { result = stod(value.string()); }\n  };\n\n  peg_parser::ParserGenerator<void, Visitor &> calculator;\n\n  auto &g = calculator;\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n\n  g[\"Expression\"] << \"Assign | Sum\";\n  g[\"Assign\"] << \"Name '=' Sum\" >> [](auto e, auto &v) { v.visitAssignment(e[0], e[1]); };\n  g[\"Sum\"] << \"Add | Subtract | Product\";\n  g[\"Product\"] << \"Multiply | Divide | Exponent\";\n  g[\"Exponent\"] << \"Power | Atomic\";\n  g[\"Atomic\"] << \"Number | Brackets | Variable\";\n  g[\"Brackets\"] << \"'(' Sum ')'\";\n  g[\"Add\"] << \"Sum '+' Product\" >> [](auto e, auto &v) { v.visitAddition(e[0], e[1]); };\n  g[\"Subtract\"] << \"Sum '-' Product\" >> [](auto e, auto &v) { v.visitSubtraction(e[0], e[1]); };\n  g[\"Multiply\"] << \"Product '*' Exponent\" >>\n      [](auto e, auto &v) { v.visitMultiplication(e[0], e[1]); };\n  g[\"Divide\"] << \"Product '/' Exponent\" >> [](auto e, auto &v) { v.visitDivision(e[0], e[1]); };\n  g[\"Power\"] << \"Atomic ('^' Exponent)\" >> [](auto e, auto &v) { v.visitPower(e[0], e[1]); };\n  g[\"Variable\"] << \"Name\" >> [](auto e, auto &v) { v.visitVariable(e); };\n  g[\"Name\"] << \"[a-zA-Z] [a-zA-Z0-9]*\";\n  g[\"Number\"] << \"'-'? [0-9]+ ('.' [0-9]+)?\" >> [](auto e, auto &v) { v.visitNumber(e); };\n\n  g.setStart(g[\"Expression\"]);\n\n  cout << \"Enter an expression to be evaluated.\\n\";\n\n  while (true) {\n    string str;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    try {\n      Visitor visitor;\n      calculator.run(str, visitor);\n      cout << str << \" = \" << visitor.result << endl;\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << string(syntax->begin, ' ');\n      cout << string(syntax->length(), '~');\n      cout << \"^\\n\";\n      cout << \"  \"\n           << \"Syntax error while parsing \" << syntax->rule->name << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "example/json_parser.cpp",
    "content": "/**\n * This example demonstrate how we can use peg_parser::parser parse standard\n * JSON. https://en.wikipedia.org/wiki/JSON#Data_types_and_syntax\n */\n\n#include <peg_parser/generator.h>\n\n#include <algorithm>\n#include <iostream>\n#include <map>\n#include <memory>\n#include <string>\n#include <variant>\n#include <vector>\n\n/** Class to store JSON objects */\nstruct JSON {\n  enum Type { NUMBER, STRING, BOOLEAN, ARRAY, OBJECT, EMPTY } type;\n  std::variant<double, std::string, bool, std::vector<JSON>, std::map<std::string, JSON>> data;\n  explicit JSON(double v) : type(NUMBER), data(v) {}\n  explicit JSON(std::string &&v) : type(STRING), data(v) {}\n  explicit JSON(bool v) : type(BOOLEAN), data(v) {}\n  explicit JSON(std::vector<JSON> &&v) : type(ARRAY), data(v) {}\n  explicit JSON(std::map<std::string, JSON> &&v) : type(OBJECT), data(v) {}\n  explicit JSON() : type(EMPTY) {}\n};\n\n/** Print JSON */\nstd::ostream &operator<<(std::ostream &stream, const JSON &json) {\n  switch (json.type) {\n    case JSON::NUMBER: {\n      stream << std::get<double>(json.data);\n      break;\n    }\n    case JSON::STRING: {\n      stream << '\"' << std::get<std::string>(json.data) << '\"';\n      break;\n    }\n    case JSON::BOOLEAN: {\n      stream << (std::get<bool>(json.data) ? \"true\" : \"false\");\n      break;\n    }\n    case JSON::ARRAY: {\n      stream << '[';\n      for (auto v : std::get<std::vector<JSON>>(json.data)) stream << v << ',';\n      stream << ']';\n      break;\n    }\n    case JSON::OBJECT: {\n      stream << '{';\n      for (auto v : std::get<std::map<std::string, JSON>>(json.data)) {\n        stream << '\"' << v.first << '\"' << ':' << v.second << ',';\n      }\n      stream << '}';\n      break;\n    }\n    case JSON::EMPTY: {\n      stream << \"null\";\n      break;\n    }\n  }\n  return stream;\n}\n\n/** Define the grammar */\npeg_parser::ParserGenerator<JSON> createJSONProgram() {\n  peg_parser::ParserGenerator<JSON> g;\n\n  g.setSeparator(g[\"Separators\"] << \"[\\t \\n]\");\n\n  g[\"JSON\"] << \"Number | String | Boolean | Array | Object | Empty\";\n\n  // Number\n  g.setProgramRule(\"Number\", peg_parser::presets::createDoubleProgram(),\n                   [](auto e) { return JSON(e.evaluate()); });\n\n  // String\n  g.setProgramRule(\"String\", peg_parser::presets::createStringProgram(\"\\\"\", \"\\\"\"),\n                   [](auto e) { return JSON(e.evaluate()); });\n\n  // Boolean\n  g[\"Boolean\"] << \"True | False\";\n  g[\"True\"] << \"'true'\" >> [](auto) { return JSON(true); };\n  g[\"False\"] << \"'false'\" >> [](auto) { return JSON(false); };\n\n  // Array\n  g[\"Array\"] << \"'[' (JSON (',' JSON)*)? ']'\" >> [](auto e) {\n    std::vector<JSON> data(e.size());\n    std::transform(e.begin(), e.end(), data.begin(), [](auto v) { return v.evaluate(); });\n    return JSON(std::move(data));\n  };\n\n  // Object\n  g[\"Object\"] << \"'{' (Pair (',' Pair)*)? '}'\" >> [](auto e) {\n    std::map<std::string, JSON> data;\n    for (auto p : e) {\n      data[std::get<std::string>(p[0].evaluate().data)] = p[1].evaluate();\n    }\n    return JSON(std::move(data));\n  };\n  g[\"Pair\"] << \"String ':' JSON\";\n\n  // Empty\n  g[\"Empty\"] << \"'null'\" >> [](auto) { return JSON(); };\n\n  g.setStart(g[\"JSON\"]);\n\n  return g;\n}\n\n/** Input */\nint main() {\n  using namespace std;\n\n  auto json = createJSONProgram();\n\n  cout << \"Enter a valid JSON expression.\\n\";\n  while (true) {\n    string str;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    try {\n      auto result = json.run(str);\n      cout << \"Parsed JSON: \" << result << endl;\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << string(syntax->begin, ' ');\n      cout << string(syntax->length(), '~');\n      cout << \"^\\n\";\n      cout << \"  \"\n           << \"Syntax error while parsing \" << syntax->rule->name << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "example/python_indentation.cpp",
    "content": "/**\n * This is a proof-of-concept example that parses python-like indentation\n * blocks.\n */\n\n#include <peg_parser/generator.h>\n\n#include <algorithm>\n#include <iostream>\n#include <unordered_set>\n#include <vector>\n\nint main() {\n  using namespace std;\n\n  struct Block {\n    size_t begin, length;\n  };\n\n  using Blocks = vector<Block>;\n\n  peg_parser::ParserGenerator<void, Blocks &> blockParser;\n\n  blockParser[\"Indentation\"] << \"' '*\";\n\n  /** storage for indentation depths */\n  std::vector<unsigned> indentations;\n\n  /** initializer is necessarry to reset the state after syntax errors */\n  blockParser[\"InitBlocks\"] << \"''\" << [&](auto &) -> bool {\n    indentations.resize(0);\n    return true;\n  };\n\n  /**\n   * matches the current block intendation\n   * note that this rule is not cacheable as results are context-dependent\n   */\n  blockParser[\"SameIndentation\"] << \"Indentation\" <<\n      [&](auto &s) -> bool { return s->length() == indentations.back(); };\n  blockParser[\"SameIndentation\"]->cacheable = false;\n\n  /** matches a deeper block intendation */\n  blockParser[\"DeeperIndentation\"]\n      << \"Indentation\" << [&](auto &s) -> bool { return s->length() > indentations.back(); };\n  blockParser[\"DeeperIndentation\"]->cacheable = false;\n\n  // enters a new block and stores the indentation\n  blockParser[\"EnterBlock\"] << \"Indentation\" << [&](auto &s) -> bool {\n    if (indentations.size() == 0 || s->length() > indentations.back()) {\n      indentations.push_back(s->length());\n      return true;\n    } else {\n      return false;\n    }\n  };\n  blockParser[\"EnterBlock\"]->cacheable = false;\n\n  /** matches a line in the current block */\n  blockParser[\"Line\"] << \"SameIndentation (!'\\n' .)+ '\\n'\";\n  blockParser.getRule(\"Line\")->cacheable = false;\n\n  /** matches an empty line */\n  blockParser[\"EmptyLine\"] << \"Indentation '\\n'\";\n\n  /** exits a block and pops the current indentation */\n  blockParser[\"ExitBlock\"] << \"''\" << [&](auto &) -> bool {\n    indentations.pop_back();\n    return true;\n  };\n  blockParser.getRule(\"ExitBlock\")->cacheable = false;\n\n  /** store all successfully parsed blocks */\n  blockParser[\"Block\"] << \"&EnterBlock Line (EmptyLine | Block | Line)* &ExitBlock\" >>\n      [](auto e, Blocks &blocks) {\n        for (auto a : e) a.evaluate(blocks);\n        blocks.push_back(Block{e.position(), e.length()});\n      };\n\n  blockParser.setStart(blockParser[\"Start\"] << \"InitBlocks Block\");\n\n  while (true) {\n    string str, input;\n    cout << \"Enter a python-like indented string. Push enter twice to parse.\" << endl;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    do {\n      input += str + '\\n';\n      cout << \"- \";\n      getline(cin, str);\n    } while (str != \"\");\n    try {\n      Blocks blocks;\n      blockParser.run(input, blocks);\n      cout << \"matched \" << blocks.size() << \" blocks.\" << endl;\n      for (auto b : blocks) {\n        cout << \"- from line \" << std::count(input.begin(), input.begin() + b.begin, '\\n') + 1;\n        cout << \" to \" << std::count(input.begin(), input.begin() + b.begin + b.length, '\\n')\n             << endl;\n      }\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << \"  \"\n           << \"Syntax error at character \" << syntax->end << \" while parsing \" << syntax->rule->name\n           << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "example/type_checker.cpp",
    "content": "/**\n *  This example shows how the parser behaviour changes with a grammar\n * ambigouity in a c-like language. It is implemented using a filter callback in\n * the `Typename` rule.\n *\n *  Note the different interpretation of `x * y` as either a pointer definition\n * or a multiplication.\n *\n *  Example input:\n *  `x * y`  -> parsed as a multiplication\n *  `type x` -> parsed as a type definition\n *  `x * y`  -> now parsed as a variable definition (pointer to `y` of type `x`)\n */\n\n#include <peg_parser/generator.h>\n\n#include <iostream>\n#include <unordered_set>\n\nint main() {\n  using namespace std;\n\n  peg_parser::ParserGenerator<std::string> typeChecker;\n  unordered_set<string> types;\n\n  auto &g = typeChecker;\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n\n  g.setStart(g[\"Expression\"] << \"Typedef | Vardef | Multiplication\");\n\n  g[\"Typedef\"] << \"'type' Name\" >> [&](auto e) {\n    types.emplace(e[0].string());\n    return \"type definition\";\n  };\n\n  g[\"Multiplication\"] << \"Variable '*' Variable\" >> [](auto) { return \"multiplication\"; };\n\n  g[\"Vardef\"] << \"Type Name\" >> [](auto) { return \"variable definition\"; };\n\n  // this rule only accepts types that have are declared in `types`\n  g[\"Typename\"] << \"Name\" << [&](auto s) -> bool {\n    auto name = s->inner[0]->string();\n    return types.find(name) != types.end();\n  };\n\n  g[\"Type\"] << \"Typename '*'?\";\n  g[\"Variable\"] << \"Name\";\n  g[\"Atomic\"] << \"Variable\";\n  g[\"Name\"] << \"[a-zA-Z] [a-zA-Z0-9]*\";\n\n  while (true) {\n    string str;\n    cout << \"> \";\n    getline(cin, str);\n    if (str == \"q\" || str == \"quit\") {\n      break;\n    }\n    try {\n      auto result = typeChecker.run(str);\n      cout << str << \" = \" << result << endl;\n    } catch (peg_parser::SyntaxError &error) {\n      auto syntax = error.syntax;\n      cout << \"  \";\n      cout << string(syntax->begin, ' ');\n      cout << string(syntax->length(), '~');\n      cout << \"^\\n\";\n      cout << \"  \"\n           << \"Syntax error while parsing \" << syntax->rule->name << endl;\n    }\n  }\n\n  return 0;\n}\n"
  },
  {
    "path": "glue/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14 FATAL_ERROR)\n\n# ---- Project ----\n\nproject(\n  PEGParserGlue\n  VERSION 1.0\n  LANGUAGES CXX\n)\n\n# ---- Include guards ----\n\nif(PROJECT_SOURCE_DIR STREQUAL PROJECT_BINARY_DIR)\n  message(\n    FATAL_ERROR\n      \"In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there.\"\n  )\nendif()\n\n# ---- Add dependencies via CPM ----\n\ninclude(../cmake/CPM.cmake)\n\nCPMFindPackage(NAME PEGParser SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)\n\nCPMAddPackage(\n  NAME Glue\n  GITHUB_REPOSITORY TheLartians/Glue\n  VERSION 1.5.1\n)\n\nCPMAddPackage(\n  NAME PackageProject.cmake\n  GITHUB_REPOSITORY TheLartians/PackageProject.cmake\n  VERSION 1.4\n)\n\n# ---- Add source files ----\n\nfile(GLOB_RECURSE headers CONFIGURE_DEPENDS \"${CMAKE_CURRENT_SOURCE_DIR}/include/*.h\")\nfile(GLOB_RECURSE sources CONFIGURE_DEPENDS \"${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp\")\n\n# ---- Create library ----\n\nadd_library(PEGParserGlue ${headers} ${sources})\n\nset_target_properties(PEGParserGlue PROPERTIES CXX_STANDARD 17)\ntarget_compile_options(PEGParserGlue PUBLIC \"$<$<BOOL:${MSVC}>:/permissive->\")\ntarget_link_libraries(PEGParserGlue PRIVATE PEGParser)\ntarget_link_libraries(PEGParserGlue PUBLIC Glue)\n\ntarget_include_directories(\n  PEGParserGlue PUBLIC $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/include>\n                       $<INSTALL_INTERFACE:include/${PROJECT_NAME}-${PROJECT_VERSION}>\n)\n\n# ---- Create an installable target ----\n\npackageProject(\n  NAME ${PROJECT_NAME}\n  NAMESPACE ${PROJECT_NAME}\n  VERSION ${PROJECT_VERSION}\n  BINARY_DIR ${PROJECT_BINARY_DIR}\n  INCLUDE_DIR ${PROJECT_SOURCE_DIR}/include\n  INCLUDE_DESTINATION include/${PROJECT_NAME}-${PROJECT_VERSION}\n  DEPENDENCIES Glue\n)\n"
  },
  {
    "path": "glue/include/peg_parser/glue.h",
    "content": "#pragma once\n\n#include <glue/value.h>\n\nnamespace peg_parser {\n  glue::MapValue glue();\n}\n"
  },
  {
    "path": "glue/source/glue.cpp",
    "content": "#include <glue/class.h>\n#include <peg_parser/generator.h>\n#include <peg_parser/glue.h>\n\n#include <stdexcept>\n\nglue::MapValue peg_parser::glue() {\n  using Any = glue::Any;\n  using AnyFunction = glue::AnyFunction;\n  using Program = peg_parser::ParserGenerator<Any, const Any &>;\n  using Expression = Program::Expression;\n\n  auto parser = glue::createAnyMap();\n\n  parser[\"Expression\"]\n      = glue::createClass<Expression>()\n            .addMethod(\"evaluate\", [](Expression &e, const Any &d) { return e.evaluate(d); })\n            .addMethod(\"size\", [](Expression &e) { return e.size(); })\n            .addMethod(\"string\", [](Expression &e) { return e.string(); })\n            .addMethod(\"position\", [](Expression &e) { return e.position(); })\n            .addMethod(\"length\", [](Expression &e) { return e.length(); })\n            .addMethod(\"get\", [](Expression &e, unsigned i) {\n              if (i < e.size()) {\n                return e[i];\n              } else {\n                throw std::runtime_error(\"invalid expression index\");\n              }\n            });\n\n  parser[\"Program\"]\n      = glue::createClass<Program>()\n            .addConstructor<>()\n            .addMethod(\"run\", [](Program &g, const std::string &str,\n                                 const Any &arg) { return g.run(str, arg); })\n            .addMethod(\"setRule\", [](Program &g, const std::string &name,\n                                     const std::string &grammar) { g.setRule(name, grammar); })\n            .addMethod(\"setRuleWithCallback\",\n                       [](Program &g, const std::string &name, const std::string &grammar,\n                          AnyFunction callback) {\n                         g.setRule(name, grammar, [callback](auto e, const Any &v) -> Any {\n                           return callback(e, v);\n                         });\n                       })\n            .addMethod(\"setStartRule\",\n                       [](Program &g, const std::string &name) { g.setStart(g.getRule(name)); })\n            .addMethod(\"setSeparatorRule\", [](Program &g, const std::string &name) {\n              g.setSeparator(g.getRule(name));\n            });\n\n  return parser;\n}\n"
  },
  {
    "path": "include/peg_parser/generator.h",
    "content": "#pragma once\n\n#include \"presets.h\"\n\nnamespace peg_parser {\n\n  template <class R = void, typename... Args> class ParserGenerator : public Program<R, Args...> {\n  private:\n    presets::GrammarProgram grammarProgram;\n    std::unordered_map<std::string, std::shared_ptr<grammar::Rule>> rules;\n    grammar::Node::Shared separatorRule;\n\n  public:\n    ParserGenerator() { grammarProgram = presets::createPEGProgram(); }\n\n    std::shared_ptr<grammar::Rule> getRule(const std::string &name) {\n      auto it = rules.find(name);\n      if (it != rules.end()) {\n        return it->second;\n      }\n      auto rule = grammar::makeRule(name, grammar::Node::Error());\n      rules[name] = rule;\n      return rule;\n    }\n\n    grammar::Node::Shared getRuleNode(const std::string &name) {\n      auto rule = grammar::Node::WeakRule(getRule(std::string(name)));\n      if (separatorRule) {\n        auto separator = grammar::Node::ZeroOrMore(separatorRule);\n        return grammar::Node::Sequence({separator, rule, separator});\n      } else {\n        return rule;\n      }\n    }\n\n    std::shared_ptr<grammar::Rule> setRule(\n        const std::string &name, const grammar::Node::Shared &grammar,\n        const typename Interpreter<R, Args...>::Callback &callback\n        = typename Interpreter<R, Args...>::Callback()) {\n      auto rule = getRule(name);\n      rule->node = grammar;\n      this->interpreter.setEvaluator(rule, callback);\n      return rule;\n    }\n\n    grammar::Node::Shared parseRule(const std::string_view &grammar) {\n      presets::RuleGetter rg = [this](const auto &name) { return getRuleNode(std::string(name)); };\n      return grammarProgram.run(grammar, rg);\n    }\n\n    std::shared_ptr<grammar::Rule> setRule(\n        const std::string &name, const std::string_view &grammar,\n        const typename Interpreter<R, Args...>::Callback &callback\n        = typename Interpreter<R, Args...>::Callback()) {\n      return setRule(name, parseRule(grammar), callback);\n    }\n\n    template <class R2, typename... Args2, class C>\n    std::shared_ptr<grammar::Rule> setProgramRule(const std::string &name,\n                                                  Program<R2, Args2...> subprogram, C &&callback) {\n      auto rule = getRule(name);\n      rule->node = grammar::Node::Rule(subprogram.parser.grammar);\n      this->interpreter.setEvaluator(\n          rule, [callback = std::forward<C>(callback), interpreter = subprogram.interpreter](\n                    auto e, Args &&...args) {\n            return callback(interpreter.interpret(e[0].syntax()), std::forward<Args>(args)...);\n          });\n      return rule;\n    }\n\n    template <class R2, typename... Args2>\n    auto setProgramRule(const std::string &name, Program<R2, Args2...> subprogram) {\n      static_assert(sizeof...(Args2) == 0);\n      static_assert(std::is_convertible<R2, R>::value);\n      auto rule = getRule(name);\n      rule->node = grammar::Node::Rule(subprogram.parser.grammar);\n      this->interpreter.setEvaluator(rule,\n                                     [interpreter = subprogram.interpreter](auto e, auto &&...) {\n                                       return R(interpreter.interpret(e[0].syntax()).evaluate());\n                                     });\n    }\n\n    std::shared_ptr<grammar::Rule> setFilteredRule(\n        const std::string &name, const std::string_view &grammar,\n        const grammar::Node::FilterCallback &filter,\n        const typename Interpreter<R, Args...>::Callback &callback\n        = typename Interpreter<R, Args...>::Callback()) {\n      return setRule(name,\n                     grammar::Node::Sequence({parseRule(grammar), grammar::Node::Filter(filter)}),\n                     callback);\n    }\n\n    void setSeparator(const std::shared_ptr<grammar::Rule> &rule) {\n      rule->hidden = true;\n      separatorRule = grammar::Node::Rule(rule);\n    }\n\n    std::shared_ptr<grammar::Rule> setSeparatorRule(const std::string &name,\n                                                    const grammar::Node::Shared &grammar) {\n      auto rule = setRule(name, grammar);\n      setSeparator(rule);\n      return rule;\n    }\n\n    std::shared_ptr<grammar::Rule> setSeparatorRule(const std::string &name,\n                                                    const std::string_view &grammar) {\n      return setSeparatorRule(name, parseRule(grammar));\n    }\n\n    void setStart(const std::shared_ptr<grammar::Rule> &rule) { this->parser.grammar = rule; }\n\n    void unsetSeparatorRule() { separatorRule.reset(); }\n\n    /** Operator overloads */\n\n    struct OperatorDelegate {\n      ParserGenerator *parent;\n      std::string ruleName;\n      std::string grammar;\n      typename Interpreter<R, Args...>::Callback callback;\n      grammar::Node::FilterCallback filter;\n\n      OperatorDelegate(ParserGenerator *p, const std::string &n) : parent(p), ruleName(n) {}\n      OperatorDelegate(const OperatorDelegate &) = delete;\n\n      OperatorDelegate &operator<<(const std::string_view &gr) {\n        this->grammar = gr;\n        return *this;\n      }\n\n      OperatorDelegate &operator>>(const typename Interpreter<R, Args...>::Callback &cp) {\n        this->callback = cp;\n        return *this;\n      }\n\n      OperatorDelegate &operator<<(const grammar::Node::FilterCallback &ft) {\n        this->filter = ft;\n        return *this;\n      }\n\n      operator std::shared_ptr<grammar::Rule>() { return parent->getRule(ruleName); }\n\n      std::shared_ptr<grammar::Rule> operator->() { return parent->getRule(ruleName); }\n\n      ~OperatorDelegate() {\n        if (grammar.size() > 0) {\n          if (filter) {\n            parent->setFilteredRule(ruleName, grammar, filter, callback);\n          } else {\n            parent->setRule(ruleName, grammar, callback);\n          }\n        }\n      }\n    };\n\n    OperatorDelegate operator[](const std::string &ruleName) {\n      return OperatorDelegate(this, ruleName);\n    }\n  };\n\n}  // namespace peg_parser\n"
  },
  {
    "path": "include/peg_parser/grammar.h",
    "content": "#pragma once\n\n#include <array>\n#include <functional>\n#include <memory>\n#include <ostream>\n#include <string>\n#include <unordered_map>\n#include <variant>\n#include <vector>\n\nnamespace peg_parser {\n\n  struct SyntaxTree;\n\n  namespace grammar {\n\n    using Letter = char;\n    struct Node;\n\n    struct Rule {\n      std::string name;\n      std::shared_ptr<Node> node;\n      bool hidden = false;\n      bool cacheable = true;\n      Rule(const std::string_view &n, const std::shared_ptr<Node> &t) : name(n), node(t) {}\n    };\n\n    inline std::shared_ptr<Rule> makeRule(const std::string_view &name,\n                                          const std::shared_ptr<Node> &node) {\n      return std::make_shared<Rule>(name, node);\n    }\n\n    struct Node {\n      using FilterCallback = std::function<bool(const std::shared_ptr<SyntaxTree> &)>;\n\n      enum class Symbol {\n        WORD,\n        ANY,\n        RANGE,\n        SEQUENCE,\n        CHOICE,\n        ZERO_OR_MORE,\n        ONE_OR_MORE,\n        OPTIONAL,\n        ALSO,\n        NOT,\n        EMPTY,\n        ERROR,\n        RULE,\n        WEAK_RULE,\n        END_OF_FILE,\n        FILTER\n      };\n\n      using Shared = std::shared_ptr<Node>;\n\n      Symbol symbol;\n\n      std::variant<std::vector<Shared>, Shared, std::weak_ptr<grammar::Rule>,\n                   std::shared_ptr<grammar::Rule>, std::string, std::array<Letter, 2>,\n                   FilterCallback>\n          data;\n\n    private:\n      Node(Symbol s) : symbol(s) {}\n      template <class T> Node(Symbol s, const T &d) : symbol(s), data(d) {}\n\n    public:\n      static Shared Word(const std::string &word) { return Shared(new Node(Symbol::WORD, word)); }\n      static Shared Any() { return Shared(new Node(Symbol::ANY)); }\n      static Shared Range(Letter a, Letter b) {\n        return Shared(new Node(Symbol::RANGE, std::array<Letter, 2>({{a, b}})));\n      }\n      static Shared Sequence(const std::vector<Shared> &args) {\n        return Shared(new Node(Symbol::SEQUENCE, args));\n      }\n      static Shared Choice(const std::vector<Shared> &args) {\n        return Shared(new Node(Symbol::CHOICE, args));\n      }\n      static Shared ZeroOrMore(const Shared &arg) {\n        return Shared(new Node(Symbol::ZERO_OR_MORE, arg));\n      }\n      static Shared OneOrMore(const Shared &arg) {\n        return Shared(new Node(Symbol::ONE_OR_MORE, arg));\n      }\n      static Shared Optional(const Shared &arg) { return Shared(new Node(Symbol::OPTIONAL, arg)); }\n      static Shared Also(const Shared &arg) { return Shared(new Node(Symbol::ALSO, arg)); }\n      static Shared Not(const Shared &arg) { return Shared(new Node(Symbol::NOT, arg)); }\n      static Shared Empty() { return Shared(new Node(Symbol::EMPTY)); }\n      static Shared Error() { return Shared(new Node(Symbol::ERROR)); }\n      static Shared Rule(const std::shared_ptr<grammar::Rule> &rule) {\n        return Shared(new Node(Symbol::RULE, rule));\n      }\n      static Shared WeakRule(const std::weak_ptr<grammar::Rule> &rule) {\n        return Shared(new Node(Symbol::WEAK_RULE, rule));\n      }\n      static Shared EndOfFile() { return Shared(new Node(Symbol::END_OF_FILE)); }\n      static Shared Filter(const FilterCallback &callback) {\n        return Shared(new Node(Symbol::FILTER, callback));\n      }\n    };\n\n    std::ostream &operator<<(std::ostream &stream, const Node &node);\n\n  }  // namespace grammar\n}  // namespace peg_parser\n"
  },
  {
    "path": "include/peg_parser/interpreter.h",
    "content": "#pragma once\n\n#include <iterator>\n#include <optional>\n#include <type_traits>\n\n#include \"parser.h\"\n\nnamespace peg_parser {\n\n  struct InterpreterError : public std::exception {\n    std::shared_ptr<SyntaxTree> tree;\n    mutable std::string buffer;\n    InterpreterError(const std::shared_ptr<SyntaxTree> &t) : tree(t) {}\n    const char *what() const noexcept override;\n  };\n\n  template <class R, typename... Args> class Interpreter {\n  public:\n    class Expression;\n    using Callback = std::function<R(const Expression &e, Args... args)>;\n\n    class Expression {\n    protected:\n      struct iterator {\n        using iterator_category = std::input_iterator_tag;\n        using value_type = Expression;\n        using pointer = Expression *;\n        using reference = Expression &;\n\n        const Expression &parent;\n        size_t idx;\n        iterator(const Expression &p, size_t i) : parent(p), idx(i) {}\n        iterator &operator++() {\n          idx++;\n          return *this;\n        }\n        Expression operator*() const { return parent[idx]; }\n        bool operator!=(const iterator &other) const {\n          return other.idx != idx || &other.parent != &parent;\n        }\n      };\n\n      const Interpreter<R, Args...> &interpreter;\n      std::shared_ptr<SyntaxTree> syntaxTree;\n\n    public:\n      Expression(const Interpreter<R, Args...> &i, std::shared_ptr<SyntaxTree> s)\n          : interpreter(i), syntaxTree(s) {}\n\n      auto size() const { return syntaxTree->inner.size(); }\n      auto view() const { return syntaxTree->view(); }\n      auto string() const { return std::string(view()); }\n      auto position() const { return syntaxTree->begin; }\n      auto length() const { return syntaxTree->length(); }\n      auto rule() const { return syntaxTree->rule; }\n      auto syntax() const { return syntaxTree; }\n\n      Expression operator[](size_t idx) const {\n        return interpreter.interpret(syntaxTree->inner[idx]);\n      }\n      std::optional<Expression> operator[](std::string_view name) const {\n        auto it = std::find_if(syntaxTree->inner.begin(), syntaxTree->inner.end(),\n                               [name](auto st) { return st->rule->name == name; });\n        if (it != syntaxTree->inner.end()) {\n          return interpreter.interpret(*it);\n        }\n        return {};\n      }\n      iterator begin() const { return iterator(*this, 0); }\n      iterator end() const { return iterator(*this, size()); }\n\n      template <class R2, typename... Args2>\n      auto evaluateBy(const Interpreter<R2, Args2...> &interpreter, Args2... args) const {\n        return interpreter.evaluate(syntaxTree, args...);\n      }\n\n      R evaluate(Args... args) const {\n        auto it = interpreter.evaluators.find(syntaxTree->rule.get());\n        if (it == interpreter.evaluators.end()) {\n          if (interpreter.defaultEvaluator) {\n            return interpreter.defaultEvaluator(*this, args...);\n          }\n          throw InterpreterError(syntaxTree);\n        }\n        return it->second(*this, args...);\n      }\n    };\n\n  private:\n    std::unordered_map<grammar::Rule *, Callback> evaluators;\n\n    static R __defaultEvaluator(const Expression &e, Args... args) {\n      size_t N = e.size();\n      if (N > 0) {\n        for (size_t i = 0; i < N - 1; ++i) {\n          e[i].evaluate(std::forward<Args>(args)...);\n        }\n        return e[N - 1].evaluate(std::forward<Args>(args)...);\n      }\n      if (!std::is_same<R, void>::value) {\n        throw InterpreterError(e.syntax());\n      }\n    };\n\n  public:\n    Callback defaultEvaluator = __defaultEvaluator;\n\n    std::shared_ptr<grammar::Rule> makeRule(const std::string_view &name,\n                                            const grammar::Node::Shared &node,\n                                            const Callback &callback) {\n      auto rule = std::make_shared<grammar::Rule>(name, node);\n      setEvaluator(rule, callback);\n      return rule;\n    }\n\n    std::shared_ptr<grammar::Rule> makeRule(const std::string &name,\n                                            const std::shared_ptr<grammar::Rule> &rule,\n                                            const Callback &callback) {\n      return makeRule(name, grammar::Node::Rule(rule), callback);\n    }\n\n    void setEvaluator(const std::shared_ptr<grammar::Rule> &rule, const Callback &callback) {\n      if (callback) {\n        evaluators[rule.get()] = callback;\n      } else {\n        auto it = evaluators.find(rule.get());\n        if (it != evaluators.end()) {\n          evaluators.erase(it);\n        }\n      }\n    }\n\n    Expression interpret(const std::shared_ptr<SyntaxTree> &tree) const {\n      return Expression{*this, tree};\n    }\n\n    R evaluate(const std::shared_ptr<SyntaxTree> &tree, Args... args) const {\n      return interpret(tree).evaluate(args...);\n    }\n  };\n\n  class SyntaxError : public std::exception {\n  private:\n    mutable std::string buffer;\n\n  public:\n    std::shared_ptr<SyntaxTree> syntax;\n\n    SyntaxError(const std::shared_ptr<SyntaxTree> &t) : syntax(t) {}\n    const char *what() const noexcept override;\n  };\n\n  template <class R, typename... Args> struct Program {\n    using Expression = typename Interpreter<R, Args...>::Expression;\n\n    Parser parser;\n    Interpreter<R, Args...> interpreter;\n\n    std::shared_ptr<SyntaxTree> parse(const std::string_view &str) const {\n      return parser.parse(str);\n    }\n\n    Expression interpret(const std::shared_ptr<SyntaxTree> &tree) const {\n      if (!tree->valid) {\n        throw SyntaxError(tree);\n      }\n      return interpreter.interpret(tree);\n    }\n\n    R run(const std::string_view &str, Args &&...args) const {\n      auto parsed = parser.parseAndGetError(str);\n      if (!parsed.syntax->valid || parsed.syntax->end < str.size()) {\n        throw SyntaxError(parsed.error);\n      }\n      return interpret(parsed.syntax).evaluate(std::forward<Args>(args)...);\n    }\n  };\n\n}  // namespace peg_parser\n"
  },
  {
    "path": "include/peg_parser/parser.h",
    "content": "#pragma once\n\n#include <stdexcept>\n\n#include \"grammar.h\"\n\nnamespace peg_parser {\n\n  struct SyntaxTree {\n    std::shared_ptr<grammar::Rule> rule;\n    std::string_view fullString;\n    std::vector<std::shared_ptr<SyntaxTree>> inner;\n    size_t begin, end;\n\n    bool valid = false;\n    bool active = true;\n    bool recursive = false;\n\n    SyntaxTree(const std::shared_ptr<grammar::Rule> &r, std::string_view s, size_t p);\n\n    size_t length() const { return end - begin; }\n    std::string_view view() const { return fullString.substr(begin, length()); }\n    std::string string() const { return std::string(view()); }\n  };\n\n  struct Parser {\n    struct Result {\n      std::shared_ptr<SyntaxTree> syntax;\n      std::shared_ptr<SyntaxTree> error;\n    };\n\n    struct GrammarError : std::exception {\n      enum Type { UNKNOWN_SYMBOL, INVALID_RULE } type;\n      grammar::Node::Shared node;\n      mutable std::string buffer;\n      GrammarError(Type t, grammar::Node::Shared n) : type(t), node(n) {}\n      const char *what() const noexcept override;\n    };\n\n    std::shared_ptr<grammar::Rule> grammar;\n\n    Parser(const std::shared_ptr<grammar::Rule> &grammar\n           = std::make_shared<grammar::Rule>(\"undefined\", grammar::Node::Error()));\n\n    static Result parseAndGetError(const std::string_view &str,\n                                   std::shared_ptr<grammar::Rule> grammar);\n    static std::shared_ptr<SyntaxTree> parse(const std::string_view &str,\n                                             std::shared_ptr<grammar::Rule> grammar);\n\n    std::shared_ptr<SyntaxTree> parse(const std::string_view &str) const;\n    Result parseAndGetError(const std::string_view &str) const;\n  };\n\n  std::ostream &operator<<(std::ostream &stream, const SyntaxTree &tree);\n\n}  // namespace peg_parser\n"
  },
  {
    "path": "include/peg_parser/presets.h",
    "content": "#pragma once\n\n#include \"interpreter.h\"\n\nnamespace peg_parser {\n\n  namespace presets {\n    Program<int> createIntegerProgram();\n    Program<float> createFloatProgram();\n    Program<double> createDoubleProgram();\n    Program<int> createHexProgram();\n    std::function<char(char)> defaultEscapeCodeCallback();\n    Program<char> createCharacterProgram(const std::function<char(char)> escapeCodeCallback\n                                         = defaultEscapeCodeCallback());\n    Program<std::string> createStringProgram(const std::string &open, const std::string &close);\n\n    using RuleGetter = const std::function<grammar::Node::Shared(const std::string_view &)> &;\n    using GrammarProgram = Program<grammar::Node::Shared, RuleGetter &>;\n    GrammarProgram createPEGProgram();\n  }  // namespace presets\n\n}  // namespace peg_parser\n"
  },
  {
    "path": "source/grammar.cpp",
    "content": "#include <easy_iterator.h>\n#include <peg_parser/grammar.h>\n#include <peg_parser/interpreter.h>\n\nusing namespace peg_parser::grammar;\n\nnamespace {\n\n  /**  alternative to `std::get` that works on iOS < 11 */\n  template <class T, class V> const T &pget(const V &v) {\n    if (auto r = std::get_if<T>(&v)) {\n      return *r;\n    } else {\n      throw std::runtime_error(\"corrupted grammar node\");\n    }\n  }\n\n}  // namespace\n\nstd::ostream &peg_parser::grammar::operator<<(std::ostream &stream, const Node &node) {\n  using Symbol = peg_parser::grammar::Node::Symbol;\n\n  switch (node.symbol) {\n    case Node::Symbol::WORD: {\n      stream << \"'\" << pget<std::string>(node.data) << \"'\";\n      break;\n    }\n\n    case Node::Symbol::ANY: {\n      stream << \".\";\n      break;\n    }\n    case Symbol::RANGE: {\n      auto &v = pget<std::array<peg_parser::grammar::Letter, 2>>(node.data);\n      stream << \"[\" << v[0] << \"-\" << v[1] << \"]\";\n      break;\n    }\n\n    case Symbol::SEQUENCE: {\n      const auto &data = pget<std::vector<Node::Shared>>(node.data);\n      stream << \"(\";\n      for (auto [i, n] : easy_iterator::enumerate(data)) {\n        stream << *n << (i + 1 == data.size() ? \"\" : \" \");\n      }\n      stream << \")\";\n      break;\n    }\n\n    case Symbol::CHOICE: {\n      stream << \"(\";\n      const auto &data = pget<std::vector<Node::Shared>>(node.data);\n      for (auto [i, n] : easy_iterator::enumerate(data)) {\n        stream << *n << (i + 1 == data.size() ? \"\" : \" | \");\n      }\n      stream << \")\";\n      break;\n    }\n\n    case Symbol::ZERO_OR_MORE: {\n      const auto &data = pget<Node::Shared>(node.data);\n      stream << *data << \"*\";\n      break;\n    }\n\n    case Symbol::ONE_OR_MORE: {\n      const auto &data = pget<Node::Shared>(node.data);\n      stream << *data << \"+\";\n      break;\n    }\n\n    case Node::Symbol::OPTIONAL: {\n      stream << *pget<Node::Shared>(node.data) << \"?\";\n      break;\n    }\n\n    case Node::Symbol::ALSO: {\n      stream << \"&\" << *pget<Node::Shared>(node.data);\n      break;\n    }\n\n    case Node::Symbol::NOT: {\n      stream << \"!\" << *pget<Node::Shared>(node.data);\n      break;\n    }\n\n    case Node::Symbol::EMPTY: {\n      stream << \"''\";\n      break;\n    }\n\n    case Node::Symbol::ERROR: {\n      stream << \"[]\";\n      break;\n    }\n\n    case Node::Symbol::RULE: {\n      auto rule = pget<std::shared_ptr<Rule>>(node.data);\n      stream << rule->name;\n      break;\n    }\n\n    case Node::Symbol::WEAK_RULE: {\n      if (auto rule = pget<std::weak_ptr<Rule>>(node.data).lock()) {\n        stream << rule->name;\n      } else {\n        stream << \"<DeletedRule>\";\n      }\n      break;\n    }\n\n    case Node::Symbol::END_OF_FILE: {\n      stream << \"<EOF>\";\n      break;\n    }\n\n    case Node::Symbol::FILTER: {\n      stream << \"<Filter>\";\n      break;\n    }\n  }\n\n  return stream;\n}\n"
  },
  {
    "path": "source/interpreter.cpp",
    "content": "#include <peg_parser/interpreter.h>\n\n#include <string>\n\nusing namespace peg_parser;\n\nconst char *InterpreterError::what() const noexcept {\n  if (buffer.size() == 0) {\n    buffer = \"no evaluator for rule '\" + tree->rule->name + \"'\";\n  }\n  return buffer.c_str();\n}\n\nconst char *SyntaxError::what() const noexcept {\n  if (buffer.size() == 0) {\n    buffer = \"syntax error at character \" + std::to_string(syntax->end + 1) + \" while parsing \"\n             + syntax->rule->name;\n  }\n  return buffer.c_str();\n}\n"
  },
  {
    "path": "source/parser.cpp",
    "content": "\n#include <easy_iterator.h>\n#include <peg_parser/parser.h>\n\n#include <algorithm>\n#include <sstream>\n#include <stack>\n#include <tuple>\n\n// Macros for debugging parsers\n// #define PEG_PARSER_TRACE\n\n#ifdef PEG_PARSER_TRACE\n#  define PEG_PARSER_DEBUG_LOG\n#  define PARSER_TRACE(X) \\\n    LOG(\"parser[\" << state.getPosition() << \",\" << state.current() << \"]: \" << __INDENT << X)\n#  define PARSER_ADVANCE(X) \\\n    LOG(\"parser[\" << getPosition() << \",\" << current() << \"]: \" << __INDENT << X)\n#else\n#  define PARSER_TRACE(X)\n#  define PARSER_ADVANCE(X)\n#endif\n\n#ifdef PEG_PARSER_DEBUG_LOG\n#  include <iostream>\n#  define LOG(X) std::cout << X << std::endl;\nnamespace {\n  std::string __INDENT = \"\";\n}\n#  define INCREASE_INDENT __INDENT = __INDENT + \"  \"\n#  define DECREASE_INDENT __INDENT = __INDENT.substr(0, __INDENT.size() - 2)\n#else\n#  define INCREASE_INDENT\n#  define DECREASE_INDENT\n#endif\n\nnamespace {\n\n  /**  alternative to `std::get` that works on iOS < 11 */\n  template <class T, class V> const T &pget(const V &v) {\n    if (auto r = std::get_if<T>(&v)) {\n      return *r;\n    } else {\n      throw std::runtime_error(\"corrupted grammar node\");\n    }\n  }\n\n}  // namespace\n\nusing namespace peg_parser;\n\nnamespace {\n\n  // Code from boost\n  // Reciprocal of the golden ratio helps spread entropy\n  //     and handles duplicates.\n  // See Mike Seymour in magic-numbers-in-boosthash-combine:\n  //     http://stackoverflow.com/questions/4948780\n\n  template <class T> inline void hash_combine(std::size_t &seed, T const &v) {\n    seed ^= std::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);\n  }\n\n  // Recursive template code derived from Matthieu M.\n  template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1> struct HashValueImpl {\n    static void apply(size_t &seed, Tuple const &tuple) {\n      HashValueImpl<Tuple, Index - 1>::apply(seed, tuple);\n      hash_combine(seed, std::get<Index>(tuple));\n    }\n  };\n\n  template <class Tuple> struct HashValueImpl<Tuple, 0> {\n    static void apply(size_t &seed, Tuple const &tuple) { hash_combine(seed, std::get<0>(tuple)); }\n  };\n\n  template <class Tuple> struct TupleHasher {\n    size_t operator()(Tuple const &tt) const {\n      size_t seed = 0;\n      HashValueImpl<Tuple>::apply(seed, tt);\n      return seed;\n    }\n  };\n\n  template <class T> std::string streamToString(T &&v) {\n    std::stringstream stream;\n    stream << v;\n    return stream.str();\n  }\n\n  class State {\n  public:\n    std::string_view string;\n\n  private:\n    size_t position;\n    using CacheKey = std::tuple<size_t, grammar::Rule *>;\n    using Cache = std::unordered_map<CacheKey, std::shared_ptr<SyntaxTree>, TupleHasher<CacheKey>>;\n    Cache cache;\n    std::shared_ptr<SyntaxTree> errorTree;\n\n  public:\n    size_t maxPosition;\n\n    State(const std::string_view &s, size_t c = 0) : string(s), position(c), maxPosition(c) {}\n\n    grammar::Letter current() { return position < string.size() ? string[position] : '\\0'; }\n\n    void advance(size_t amount = 1) {\n      position += amount;\n      if (position > string.size()) {\n        position = string.size();\n      }\n      if (position > maxPosition) {\n        maxPosition = position;\n      }\n      PARSER_ADVANCE(\"advancing \" << amount << \" to \" << position << \": '\" << current() << \"'\");\n    }\n\n    void setPosition(size_t p) {\n      if (p == position) {\n        return;\n      }\n      position = p;\n      PARSER_ADVANCE(\"resetting to \" << position << \": '\" << current() << \"'\");\n    }\n\n    size_t getPosition() { return position; }\n\n    struct Saved {\n      size_t position;\n      size_t innerCount;\n    };\n\n    Saved save() { return Saved{position, stack.size() > 0 ? stack.back()->inner.size() : 0}; }\n\n    void load(const Saved &s) {\n      if (stack.size() > 0) {\n        stack.back()->end = getPosition();\n        stack.back()->inner.resize(s.innerCount);\n      }\n      setPosition(s.position);\n    }\n\n    bool isAtEnd() { return position == string.size(); }\n\n    std::shared_ptr<SyntaxTree> getCached(const std::shared_ptr<grammar::Rule> &rule) {\n      auto it = cache.find(std::make_pair(position, rule.get()));\n      if (it != cache.end()) return it->second;\n      return std::shared_ptr<SyntaxTree>();\n    }\n\n    void addToCache(const std::shared_ptr<SyntaxTree> &tree) {\n      cache[std::make_pair(tree->begin, tree->rule.get())] = tree;\n    }\n\n    const Cache &getCache() { return cache; }\n\n    void removeFromCache(const std::shared_ptr<SyntaxTree> &tree) {\n      auto it = cache.find(std::make_pair(tree->begin, tree->rule.get()));\n      if (it != cache.end()) {\n        cache.erase(it);\n      }\n    }\n\n    void addInnerSyntaxTree(const std::shared_ptr<SyntaxTree> &tree) {\n      if (stack.size() > 0 && !tree->rule->hidden) {\n        stack.back()->inner.push_back(tree);\n      }\n    }\n\n    std::vector<std::shared_ptr<SyntaxTree>> stack;\n\n    std::shared_ptr<SyntaxTree> getErrorTree() { return errorTree; }\n\n    void trackError(const std::shared_ptr<SyntaxTree> &tree) {\n      if (!tree) {\n        return;\n      }\n      if (tree->length() > 0 && !tree->rule->hidden) {\n        if (errorTree) {\n          if (tree->end >= errorTree->end) {\n            errorTree = tree;\n          }\n        } else {\n          errorTree = tree;\n        }\n      }\n    }\n  };\n\n  bool parse(const std::shared_ptr<grammar::Node> &node, State &state);\n\n  std::shared_ptr<SyntaxTree> parseRule(const std::shared_ptr<grammar::Rule> &rule, State &state,\n                                        bool useCache = true) {\n    PARSER_TRACE(\"enter rule \" << rule->name);\n    INCREASE_INDENT;\n\n    if (useCache && rule->cacheable) {\n      auto cached = state.getCached(rule);\n\n      if (cached) {\n        PARSER_TRACE(\"cached\");\n        if (cached->valid) {\n          state.addInnerSyntaxTree(cached);\n          state.advance();\n          state.setPosition(cached->end);\n        } else {\n          PARSER_TRACE(\"failed\");\n          if (cached->active && !cached->recursive) {\n            PARSER_TRACE(\"found left recursion\");\n            cached->recursive = true;\n          }\n        }\n        DECREASE_INDENT;\n        PARSER_TRACE(\"exit rule \" << rule->name);\n        return cached;\n      }\n    }\n\n    auto syntaxTree = std::make_shared<SyntaxTree>(rule, state.string, state.getPosition());\n\n    if (useCache) {\n      state.addToCache(syntaxTree);\n    }\n\n    auto saved = state.save();\n    state.stack.push_back(syntaxTree);\n    syntaxTree->valid = parse(rule->node, state);\n    syntaxTree->end = state.getPosition();\n    syntaxTree->active = false;\n    state.stack.pop_back();\n\n    if (syntaxTree->valid) {\n      if (useCache && syntaxTree->recursive) {\n        PARSER_TRACE(\"enter left recursion: \" << rule->name);\n        while (true) {\n          State recursionState(state.string, syntaxTree->begin);\n          recursionState.trackError(state.getErrorTree());\n          // Copy the cache except the currect position to the recursion state\n          // TODO: keeping the current state and modifying the cache in place is\n          // probably much more efficient.\n          for (auto &cached : state.getCache()) {\n            if (std::get<0>(cached.first) != syntaxTree->begin) {\n              recursionState.addToCache(cached.second);\n            }\n          }\n          recursionState.addToCache(syntaxTree);\n          auto tmp = parseRule(rule, recursionState, false);\n          state.trackError(recursionState.getErrorTree());\n          if (tmp->valid && tmp->end > syntaxTree->end) {\n            PARSER_TRACE(\"parsed left recursion\");\n            syntaxTree = tmp;\n            if (useCache) {\n              state.addToCache(syntaxTree);\n            }\n            state.setPosition(tmp->end);\n          } else {\n            break;\n          }\n        }\n        PARSER_TRACE(\"exit left recursion\");\n      }\n\n      state.addInnerSyntaxTree(syntaxTree);\n    } else {\n      state.trackError(syntaxTree);\n      state.load(saved);\n    }\n\n    DECREASE_INDENT;\n    PARSER_TRACE(\"exit rule \" << rule->name);\n\n    return syntaxTree;\n  }\n\n  bool parse(const std::shared_ptr<grammar::Node> &node, State &state) {\n    using Node = peg_parser::grammar::Node;\n    using Symbol = Node::Symbol;\n\n    PARSER_TRACE(\"parsing \" << *node);\n\n    auto c = state.current();\n    switch (node->symbol) {\n      case peg_parser::grammar::Node::Symbol::WORD: {\n        auto saved = state.save();\n        for (auto c : pget<std::string>(node->data)) {\n          if (state.current() != c) {\n            state.load(saved);\n            PARSER_TRACE(\"failed\");\n            return false;\n          }\n          state.advance();\n        }\n        return true;\n      }\n\n      case peg_parser::grammar::Node::Symbol::ANY: {\n        if (state.isAtEnd()) {\n          PARSER_TRACE(\"failed\");\n          return false;\n        } else {\n          state.advance();\n          return true;\n        }\n      }\n\n      case Symbol::RANGE: {\n        auto &v = pget<std::array<grammar::Letter, 2>>(node->data);\n        if (c >= v[0] && c <= v[1]) {\n          state.advance();\n          return true;\n        } else {\n          PARSER_TRACE(\"failed\");\n          return false;\n        }\n      }\n\n      case Symbol::SEQUENCE: {\n        auto saved = state.save();\n        for (auto n : pget<std::vector<grammar::Node::Shared>>(node->data)) {\n          if (!parse(n, state)) {\n            state.load(saved);\n            return false;\n          }\n        }\n        return true;\n      }\n\n      case Symbol::CHOICE: {\n        for (auto n : pget<std::vector<grammar::Node::Shared>>(node->data)) {\n          if (parse(n, state)) {\n            return true;\n          }\n        }\n        return false;\n      }\n\n      case Symbol::ZERO_OR_MORE: {\n        auto data = pget<Node::Shared>(node->data);\n        while (parse(data, state)) {\n        }\n        return true;\n      }\n\n      case peg_parser::grammar::Node::Symbol::ONE_OR_MORE: {\n        const auto &data = pget<Node::Shared>(node->data);\n        if (!parse(data, state)) {\n          return false;\n        }\n        while (parse(data, state)) {\n        }\n        return true;\n      }\n\n      case peg_parser::grammar::Node::Symbol::OPTIONAL: {\n        const auto &data = pget<Node::Shared>(node->data);\n        parse(data, state);\n        return true;\n      }\n\n      case peg_parser::grammar::Node::Symbol::ALSO: {\n        const auto &data = pget<Node::Shared>(node->data);\n        auto saved = state.save();\n        auto result = parse(data, state);\n        state.load(saved);\n        return result;\n      }\n\n      case peg_parser::grammar::Node::Symbol::NOT: {\n        const auto &data = pget<Node::Shared>(node->data);\n        auto saved = state.save();\n        auto result = parse(data, state);\n        state.load(saved);\n        return !result;\n      }\n\n      case peg_parser::grammar::Node::Symbol::ERROR: {\n        return false;\n      }\n\n      case peg_parser::grammar::Node::Symbol::EMPTY: {\n        return true;\n      }\n\n      case peg_parser::grammar::Node::Symbol::RULE: {\n        const auto &rule = pget<std::shared_ptr<grammar::Rule>>(node->data);\n        return parseRule(rule, state)->valid;\n      }\n\n      case peg_parser::grammar::Node::Symbol::WEAK_RULE: {\n        const auto &data = pget<std::weak_ptr<grammar::Rule>>(node->data);\n        if (auto rule = data.lock()) {\n          return parseRule(rule, state)->valid;\n        } else {\n          throw Parser::GrammarError(Parser::GrammarError::INVALID_RULE, node);\n        }\n      }\n\n      case peg_parser::grammar::Node::Symbol::END_OF_FILE: {\n        auto res = state.isAtEnd();\n        if (!res) {\n          PARSER_TRACE(\"failed\");\n        }\n        return res;\n      }\n\n      case peg_parser::grammar::Node::Symbol::FILTER: {\n        const auto &callback = pget<grammar::Node::FilterCallback>(node->data);\n        bool res;\n        if (state.stack.size() > 0) {\n          auto tree = state.stack.back();\n          tree->end = state.getPosition();\n          res = callback(tree);\n          state.setPosition(tree->end);\n        } else {\n          res = false;\n        }\n        if (!res) {\n          PARSER_TRACE(\"failed\");\n        }\n        return res;\n      }\n    }\n\n    throw Parser::GrammarError(Parser::GrammarError::UNKNOWN_SYMBOL, node);\n  }\n\n}  // namespace\n\nSyntaxTree::SyntaxTree(const std::shared_ptr<grammar::Rule> &r, std::string_view s, size_t p)\n    : rule(r), fullString(s), begin(p), end(p), valid(false), active(true) {}\n\nconst char *peg_parser::Parser::GrammarError::what() const noexcept {\n  if (buffer.size() == 0) {\n    std::string typeName;\n    switch (type) {\n      case UNKNOWN_SYMBOL:\n        typeName = \"UNKNOWN_SYMBOL\";\n        break;\n      case INVALID_RULE:\n        typeName = \"INVALID_RULE\";\n        break;\n    }\n    buffer = \"internal error in grammar node (\" + typeName + \"): \" + streamToString(*node);\n  }\n  return buffer.c_str();\n}\n\nParser::Parser(const std::shared_ptr<grammar::Rule> &g) : grammar(g) {}\n\nParser::Result Parser::parseAndGetError(const std::string_view &str,\n                                        std::shared_ptr<grammar::Rule> grammar) {\n  State state(str);\n  PARSER_TRACE(\"Begin parsing of: '\" << str << \"'\");\n  auto result = parseRule(grammar, state);\n  auto error = state.getErrorTree();\n  if (!error) {\n    error = result;\n  }\n  return Parser::Result{result, error};\n}\n\nstd::shared_ptr<SyntaxTree> Parser::parse(const std::string_view &str,\n                                          std::shared_ptr<grammar::Rule> grammar) {\n  return parseAndGetError(str, grammar).syntax;\n}\n\nstd::shared_ptr<SyntaxTree> Parser::parse(const std::string_view &str) const {\n  return parse(str, grammar);\n}\n\nParser::Result Parser::parseAndGetError(const std::string_view &str) const {\n  return parseAndGetError(str, grammar);\n}\n\nstd::ostream &peg_parser::operator<<(std::ostream &stream, const SyntaxTree &tree) {\n  stream << tree.rule->name << '(';\n  if (tree.inner.size() == 0) {\n    stream << '\\'' << tree.view() << '\\'';\n  } else {\n    for (auto &&[i, arg] : easy_iterator::enumerate(tree.inner)) {\n      stream << (*arg) << (i + 1 == tree.inner.size() ? \"\" : \", \");\n    }\n  }\n  stream << ')';\n  return stream;\n}\n"
  },
  {
    "path": "source/presets.cpp",
    "content": "#include <peg_parser/presets.h>\n\n#include <string>\n\nusing namespace peg_parser;\nusing namespace peg_parser::presets;\nusing GN = grammar::Node;\n\nProgram<int> presets::createIntegerProgram() {\n  Program<int> program;\n  auto pattern = GN::Sequence({GN::Optional(GN::Word(\"-\")), GN::OneOrMore(GN::Range('0', '9'))});\n  program.parser.grammar = program.interpreter.makeRule(\n      \"Number\", pattern, [](auto e) { return std::stoi(e.string()); });\n  return program;\n}\n\nnamespace {\n\n  grammar::Node::Shared createFloatGrammar() {\n    return GN::Sequence(\n        {GN::Optional(GN::Word(\"-\")), GN::OneOrMore(GN::Range('0', '9')),\n         GN::Optional(GN::Sequence({GN::Word(\".\"), GN::OneOrMore(GN::Range('0', '9'))})),\n         GN::Optional(\n             GN::Sequence({GN::Choice({GN::Word(\"e\"), GN::Word(\"E\")}), GN::Optional(GN::Word(\"-\")),\n                           GN::OneOrMore(GN::Range('0', '9'))}))});\n  }\n\n}  // namespace\n\nProgram<float> presets::createFloatProgram() {\n  Program<float> program;\n  program.parser.grammar = program.interpreter.makeRule(\n      \"Float\", createFloatGrammar(), [](auto e) { return std::stof(e.string()); });\n  return program;\n}\n\nProgram<double> presets::createDoubleProgram() {\n  Program<double> program;\n  program.parser.grammar = program.interpreter.makeRule(\n      \"Float\", createFloatGrammar(), [](auto e) { return std::stod(e.string()); });\n  return program;\n}\n\nProgram<int> presets::createHexProgram() {\n  Program<int> program;\n  auto pattern = GN::Sequence(\n      {GN::OneOrMore(GN::Choice({GN::Range('0', '9'), GN::Range('a', 'f'), GN::Range('A', 'F')}))});\n  program.parser.grammar = program.interpreter.makeRule(\n      \"Hex\", pattern, [](auto e) { return std::stoi(e.string(), 0, 16); });\n  return program;\n}\n\nstd::function<char(char)> presets::defaultEscapeCodeCallback() {\n  std::unordered_map<char, char> codes{{'n', '\\n'}, {'t', '\\t'}, {'0', '\\0'}};\n  return [codes](char c) {\n    auto it = codes.find(c);\n    if (it != codes.end()) {\n      return it->second;\n    } else {\n      return c;\n    }\n  };\n}\n\nProgram<char> presets::createCharacterProgram(const std::function<char(char)> escapeCodeCallback) {\n  Program<char> program;\n\n  auto backslash = GN::Word(\"\\\\\");\n\n  auto escaped = GN::Rule(program.interpreter.makeRule(\n      \"Escaped\", GN::Sequence({backslash, GN::Any()}),\n      [escapeCodeCallback](auto e) { return escapeCodeCallback(e.view()[1]); }));\n\n  auto numberParser = createHexProgram();\n  auto escapedCode = GN::Rule(program.interpreter.makeRule(\n      \"escapedCode\", GN::Sequence({backslash, GN::Rule(numberParser.parser.grammar)}),\n      [interpreter = numberParser.interpreter](auto e) {\n        return char(0 + e[0].evaluateBy(interpreter));\n      }));\n\n  auto character = GN::Rule(program.interpreter.makeRule(\"SingleCharacter\", GN::Any(),\n                                                         [](auto e) { return e.view()[0]; }));\n\n  program.parser.grammar\n      = program.interpreter.makeRule(\"Character\", GN::Choice({escapedCode, escaped, character}),\n                                     [](auto e) { return e[0].evaluate(); });\n\n  return program;\n}\n\nProgram<std::string> presets::createStringProgram(const std::string &open,\n                                                  const std::string &close) {\n  Program<std::string> program;\n\n  auto characterProgram = createCharacterProgram();\n\n  auto pattern\n      = GN::Sequence({GN::Word(open),\n                      GN::ZeroOrMore(GN::Sequence(\n                          {GN::Not(GN::Word(close)), GN::Rule(characterProgram.parser.grammar)})),\n                      GN::Word(close)});\n\n  program.parser.grammar = program.interpreter.makeRule(\n      \"String\", pattern, [interpreter = characterProgram.interpreter](auto e) {\n        std::string res;\n        for (auto c : e) {\n          res += c.evaluateBy(interpreter);\n        }\n        return res;\n      });\n\n  return program;\n}\n\nGrammarProgram presets::createPEGProgram() {\n  GrammarProgram program;\n\n  auto whitespaceRule\n      = makeRule(\"Whitespace\", GN::ZeroOrMore(GN::Choice({GN::Word(\" \"), GN::Word(\"\\t\")})));\n  whitespaceRule->hidden = true;\n  auto whitespace = GN::Rule(whitespaceRule);\n  auto withWhitespace = [whitespace](GN::Shared node) {\n    return GN::Sequence({whitespace, node, whitespace});\n  };\n\n  auto stringProgram = createStringProgram(\"'\", \"'\");\n\n  auto expressionRule = program.interpreter.makeRule(\n      \"Expression\", GN::Empty(), [](auto e, auto &g) { return e[0].evaluate(g); });\n  auto expression = GN::WeakRule(expressionRule);\n\n  auto atomicRule = program.interpreter.makeRule(\"Atomic\", GN::Empty(),\n                                                 [](auto e, auto &g) { return e[0].evaluate(g); });\n  auto atomic = GN::WeakRule(atomicRule);\n\n  auto endOfFile = GN::Rule(program.interpreter.makeRule(\n      \"EndOfFile\", GN::Word(\"<EOF>\"), [](auto, auto &) { return GN::EndOfFile(); }));\n\n  auto any = GN::Rule(\n      program.interpreter.makeRule(\"Any\", GN::Word(\".\"), [](auto, auto &) { return GN::Any(); }));\n\n  auto selectCharacterProgram = createCharacterProgram();\n  auto selectCharacter = GN::Sequence({GN::Not(GN::Choice({GN::Word(\"-\"), GN::Word(\"]\")})),\n                                       GN::Rule(selectCharacterProgram.parser.grammar)});\n  auto range = GN::Rule(program.interpreter.makeRule(\n      \"Range\", GN::Sequence({selectCharacter, GN::Word(\"-\"), selectCharacter}),\n      [interpreter = selectCharacterProgram.interpreter](auto e, auto &) {\n        return GN::Range(e[0].evaluateBy(interpreter), e[1].evaluateBy(interpreter));\n      }));\n  auto singeCharacter = GN::Rule(program.interpreter.makeRule(\n      \"Character\", selectCharacter,\n      [interpreter = selectCharacterProgram.interpreter](auto e, auto &) {\n        return GN::Word(std::string(1, e[0].evaluateBy(interpreter)));\n      }));\n  auto selectSequence = GN::Sequence(\n      {GN::Word(\"[\"), GN::ZeroOrMore(GN::Choice({range, singeCharacter})), GN::Word(\"]\")});\n  auto select\n      = GN::Rule(program.interpreter.makeRule(\"Select\", selectSequence, [](auto e, auto &g) {\n          if (e.size() == 0) {\n            return GN::Error();\n          }\n          if (e.size() == 1) {\n            return e[0].evaluate(g);\n          }\n          std::vector<GN::Shared> args;\n          for (auto c : e) {\n            args.push_back(c.evaluate(g));\n          }\n          return GN::Choice(args);\n        }));\n\n  auto word = GN::Rule(\n      program.interpreter.makeRule(\"Word\", stringProgram.parser.grammar,\n                                   [interpreter = stringProgram.interpreter](auto e, auto &) {\n                                     auto word = e[0].evaluateBy(interpreter);\n                                     if (word.size() == 0) {\n                                       return GN::Empty();\n                                     } else {\n                                       return GN::Word(e[0].evaluateBy(interpreter));\n                                     }\n                                   }));\n\n  auto ruleName = GN::Sequence({GN::Not(GN::Range('0', '9')),\n                                GN::OneOrMore(GN::Choice({GN::Range('a', 'z'), GN::Range('A', 'Z'),\n                                                          GN::Range('0', '9'), GN::Word(\"_\")}))});\n  auto rule = GN::Rule(\n      program.interpreter.makeRule(\"Rule\", ruleName, [](auto e, auto &g) { return g(e.view()); }));\n\n  auto brackets = GN::Sequence({GN::Word(\"(\"), expression, GN::Word(\")\")});\n\n  auto andPredicate = GN::Rule(\n      program.interpreter.makeRule(\"AndPredicate\", GN::Sequence({GN::Word(\"&\"), atomic}),\n                                   [](auto e, auto &g) { return GN::Also(e[0].evaluate(g)); }));\n\n  auto notPredicate = GN::Rule(\n      program.interpreter.makeRule(\"NotPredicate\", GN::Sequence({GN::Word(\"!\"), atomic}),\n                                   [](auto e, auto &g) { return GN::Not(e[0].evaluate(g)); }));\n\n  atomicRule->node = withWhitespace(\n      GN::Choice({andPredicate, notPredicate, word, brackets, endOfFile, any, select, rule}));\n\n  auto predicate\n      = GN::Rule(makeRule(\"Predicate\", GN::Choice({GN::Word(\"+\"), GN::Word(\"*\"), GN::Word(\"?\")})));\n\n  auto unary = withWhitespace(GN::Rule(program.interpreter.makeRule(\n      \"Unary\", GN::Sequence({GN::Rule(atomicRule), GN::Optional(predicate)}), [](auto e, auto &g) {\n        auto inner = e[0].evaluate(g);\n        if (e.size() == 1) {\n          return inner;\n        }\n        auto op = e[1].view()[0];\n        if (op == '*') {\n          return GN::ZeroOrMore(inner);\n        }\n        if (op == '+') {\n          return GN::OneOrMore(inner);\n        }\n        if (op == '?') {\n          return GN::Optional(inner);\n        }\n        throw std::runtime_error(\"unexpected unary operator\");\n      })));\n\n  auto sequence = GN::Rule(program.interpreter.makeRule(\n      \"Sequence\", GN::Sequence({unary, GN::ZeroOrMore(unary)}), [](auto e, auto &g) {\n        if (e.size() == 1) {\n          return e[0].evaluate(g);\n        }\n        std::vector<GN::Shared> args;\n        for (auto c : e) {\n          args.push_back(c.evaluate(g));\n        }\n        return GN::Sequence(args);\n      }));\n\n  auto choice = GN::Rule(program.interpreter.makeRule(\n      \"Choice\", GN::Sequence({sequence, GN::ZeroOrMore(GN::Sequence({GN::Word(\"|\"), sequence}))}),\n      [](auto e, auto &g) {\n        if (e.size() == 1) {\n          return e[0].evaluate(g);\n        }\n        std::vector<GN::Shared> args;\n        for (auto c : e) {\n          args.push_back(c.evaluate(g));\n        }\n        return GN::Choice(args);\n      }));\n\n  expressionRule->node = withWhitespace(choice);\n\n  auto fullExpression = program.interpreter.makeRule(\n      \"FullExpression\", GN::Sequence({GN::Rule(expressionRule), GN::EndOfFile()}),\n      [](auto e, auto &g) { return e[0].evaluate(g); });\n  program.parser.grammar = fullExpression;\n\n  return program;\n}\n"
  },
  {
    "path": "test/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.5 FATAL_ERROR)\n\nproject(PEGParserTests LANGUAGES CXX)\n\n# ---- Options ----\n\noption(ENABLE_TEST_COVERAGE \"Enable test coverage\" OFF)\noption(TEST_INSTALLED_VERSION \"Test the version found by find_package\" OFF)\n\n# --- Import tools ----\n\ninclude(../cmake/tools.cmake)\n\n# ---- Dependencies ----\n\ninclude(../cmake/CPM.cmake)\n\nCPMAddPackage(\n  NAME Catch2\n  GITHUB_REPOSITORY catchorg/Catch2\n  VERSION 2.13.4\n)\n\nif(TEST_INSTALLED_VERSION)\n  find_package(PEGParser REQUIRED)\n  find_package(PEGParserGlue REQUIRED)\nelse()\n  CPMAddPackage(NAME PEGParser SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/..)\n  CPMAddPackage(NAME PEGParserGlue SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/../glue)\nendif()\n\nCPMAddPackage(\n  NAME Format.cmake\n  GITHUB_REPOSITORY TheLartians/Format.cmake\n  VERSION 1.6\n  OPTIONS \"FORMAT_CHECK_CMAKE ON\"\n)\n\n# ---- Create binary ----\n\nfile(GLOB sources CONFIGURE_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp)\nadd_executable(PEGParserTests ${sources})\ntarget_link_libraries(PEGParserTests Catch2 PEGParser::PEGParser PEGParserGlue::PEGParserGlue)\n\nset_target_properties(PEGParserTests PROPERTIES CXX_STANDARD 17)\n\n# enable compiler warnings\nif(NOT TEST_INSTALLED_VERSION)\n  if(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR CMAKE_CXX_COMPILER_ID MATCHES \"GNU\")\n    target_compile_options(PEGParser PUBLIC -Wall -pedantic -Wextra -Werror)\n  elseif(MSVC)\n    target_compile_options(PEGParser PUBLIC /W4 /WX /wd4456)\n    target_compile_definitions(PEGParserTests PUBLIC DOCTEST_CONFIG_USE_STD_HEADERS)\n  endif()\nendif()\n\n# ---- Add PEGParserTests ----\n\nenable_testing()\n\ninclude(${Catch2_SOURCE_DIR}/contrib/Catch.cmake)\ncatch_discover_tests(PEGParserTests)\n\n# ---- code coverage ----\n\nif(ENABLE_TEST_COVERAGE)\n  target_compile_options(PEGParser PUBLIC -O0 -g -fprofile-arcs -ftest-coverage)\n  target_link_options(PEGParser PUBLIC -fprofile-arcs -ftest-coverage)\nendif()\n"
  },
  {
    "path": "test/source/example.cpp",
    "content": "#include <catch2/catch.hpp>\n\n// clang-format off\n#include <peg_parser/generator.h>\n#include <iostream>\n\nvoid example() {\n  peg_parser::ParserGenerator<float> g;\n\n  // Define grammar and evaluation rules\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n  g[\"Sum\"     ] << \"Add | Subtract | Product\";\n  g[\"Product\" ] << \"Multiply | Divide | Atomic\";\n  g[\"Atomic\"  ] << \"Number | '(' Sum ')'\";\n  g[\"Add\"     ] << \"Sum '+' Product\"    >> [](auto e){ return e[0].evaluate() + e[1].evaluate(); };\n  g[\"Subtract\"] << \"Sum '-' Product\"    >> [](auto e){ return e[0].evaluate() - e[1].evaluate(); };\n  g[\"Multiply\"] << \"Product '*' Atomic\" >> [](auto e){ return e[0].evaluate() * e[1].evaluate(); };\n  g[\"Divide\"  ] << \"Product '/' Atomic\" >> [](auto e){ return e[0].evaluate() / e[1].evaluate(); };\n  g[\"Number\"  ] << \"'-'? [0-9]+ ('.' [0-9]+)?\" >> [](auto e){ return stof(e.string()); };\n  g.setStart(g[\"Sum\"]);\n\n  // Execute a string\n  auto input = \"1 + 2 * (3+4)/2 - 3\";\n  float result = g.run(input); // -> 5\n  std::cout << input << \" = \" << result << std::endl;\n}\n// clang-format on\n\nTEST_CASE(\"Example\") {\n  auto orig_buf = std::cout.rdbuf();\n  std::cout.rdbuf(NULL);\n  CHECK_NOTHROW(example());\n  std::cout.rdbuf(orig_buf);\n}\n"
  },
  {
    "path": "test/source/glue.cpp",
    "content": "#include <glue/context.h>\n#include <glue/keys.h>\n#include <peg_parser/glue.h>\n\n#include <catch2/catch.hpp>\n#include <unordered_map>\n\nTEST_CASE(\"Extension\") {\n  using namespace peg_parser;\n\n  auto parserGlue = peg_parser::glue();\n  glue::Context context;\n  context.addRootMap(parserGlue);\n\n  auto programGlue = parserGlue[\"Program\"];\n  auto expressionGlue = parserGlue[\"Expression\"];\n\n  auto createProgram = programGlue[glue::keys::constructorKey];\n  auto setRule = programGlue[\"setRule\"];\n  auto setSeparator = programGlue[\"setSeparatorRule\"];\n  auto setStart = programGlue[\"setStartRule\"];\n  auto setRuleWithCallback = programGlue[\"setRuleWithCallback\"];\n  auto run = programGlue[\"run\"];\n  auto evaluate = expressionGlue[\"evaluate\"];\n  auto get = expressionGlue[\"get\"];\n  auto string = expressionGlue[\"string\"];\n  auto size = expressionGlue[\"size\"];\n  auto position = expressionGlue[\"position\"];\n  auto length = expressionGlue[\"length\"];\n\n  using VariableMap = std::unordered_map<std::string, float>;\n\n  auto program = createProgram();\n  REQUIRE_NOTHROW(setRule(program, \"Whitespace\", \"[\\t ]\"));\n  REQUIRE_NOTHROW(setSeparator(program, \"Whitespace\"));\n  REQUIRE_NOTHROW(setStart(program, \"Sum\"));\n  REQUIRE_NOTHROW(setRule(program, \"Sum\", \"Add | Subtract | Product\"));\n  REQUIRE_NOTHROW(setRule(program, \"Product\", \"Multiply | Divide | Atomic\"));\n  REQUIRE_NOTHROW(setRule(program, \"Atomic\", \"Number | '(' Sum ')'\"));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(\n      program, \"Add\", \"Sum '+' Product\", glue::AnyFunction([=](const glue::Any &e, VariableMap &d) {\n        return evaluate(get(e, 0), d)->get<float>() + evaluate(get(e, 1), d)->get<float>();\n      })));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(program, \"Subtract\", \"Sum '-' Product\",\n                                      glue::AnyFunction([=](const glue::Any &e, VariableMap &d) {\n                                        return evaluate(get(e, 0), d)->get<float>()\n                                               - evaluate(get(e, 1), d)->get<float>();\n                                      })));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(program, \"Multiply\", \"Product '*' Atomic\",\n                                      glue::AnyFunction([=](const glue::Any &e, VariableMap &d) {\n                                        return evaluate(get(e, 0), d)->get<float>()\n                                               * evaluate(get(e, 1), d)->get<float>();\n                                      })));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(program, \"Divide\", \"Product '/' Atomic\",\n                                      glue::AnyFunction([=](const glue::Any &e, VariableMap &d) {\n                                        return evaluate(get(e, 0), d)->get<float>()\n                                               / evaluate(get(e, 1), d)->get<float>();\n                                      })));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(program, \"Number\", \"'-'? [0-9]+ ('.' [0-9]+)?\",\n                                      glue::AnyFunction([=](const glue::Any &e, VariableMap &) {\n                                        REQUIRE(size(e)->get<int>() == 0);\n                                        REQUIRE(position(e)->get<int>() >= 0);\n                                        REQUIRE(length(e)->get<size_t>()\n                                                == string(e)->get<std::string>().size());\n                                        return float(stof(string(e)->get<std::string>()));\n                                      })));\n\n  REQUIRE_NOTHROW(setRuleWithCallback(program, \"Variable\", \"[a-zA-Z]+\",\n                                      glue::AnyFunction([=](const glue::Any &e, VariableMap &d) {\n                                        auto &vars = d;\n                                        return vars[string(e)->get<std::string>()];\n                                      })));\n\n  VariableMap variables;\n  REQUIRE(run(program, \"42\", variables)->get<float>() == Approx(42));\n  REQUIRE(run(program, \"2+3\", variables)->get<float>() == Approx(5));\n  REQUIRE(run(program, \"2*3\", variables)->get<float>() == Approx(6));\n  REQUIRE(run(program, \"1+2+3\", variables)->get<float>() == Approx(6));\n  REQUIRE(run(program, \"1+2*3\", variables)->get<float>() == Approx(7));\n  REQUIRE(run(program, \"1+2-3\", variables)->get<float>() == Approx(0));\n  REQUIRE(run(program, \"2*2/4*3\", variables)->get<float>() == Approx(3));\n  REQUIRE(run(program, \"1 - 2*3/2 + 4\", variables)->get<float>() == Approx(2));\n  REQUIRE(run(program, \"1 + 2 * (3+4)/ 2 - 3\", variables)->get<float>() == Approx(5));\n}\n"
  },
  {
    "path": "test/source/main.cpp",
    "content": "#define CATCH_CONFIG_MAIN\n\n#include <catch2/catch.hpp>\n"
  },
  {
    "path": "test/source/parser.cpp",
    "content": "#include <peg_parser/generator.h>\n\n#include <catch2/catch.hpp>\n#include <numeric>\n#include <sstream>\n#include <string>\n#include <tuple>\n\ntemplate <class T> std::string stream_to_string(const T &obj) {\n  std::stringstream stream;\n  stream << obj;\n  return stream.str();\n}\n\nusing namespace peg_parser;\n\nTEST_CASE(\"Number Program\") {\n  auto program = presets::createIntegerProgram();\n  REQUIRE(program.run(\"42\") == 42);\n  REQUIRE(program.run(\"-3\") == -3);\n  REQUIRE_THROWS(program.run(\"42r\"));\n  REQUIRE_THROWS(program.run(\"not a number\"));\n}\n\nTEST_CASE(\"Float Program\") {\n  auto testFloatProgram = [](auto p) {\n    REQUIRE(p.run(\"42\") == Approx(42));\n    REQUIRE(p.run(\"3.1412\") == Approx(3.1412));\n    REQUIRE(p.run(\"2E10\") == Approx(2E10));\n    REQUIRE(p.run(\"1.4e-3\") == Approx(1.4e-3));\n  };\n\n  testFloatProgram(presets::createFloatProgram());\n  testFloatProgram(presets::createDoubleProgram());\n}\n\nTEST_CASE(\"Hex Program\") {\n  auto parser = presets::createHexProgram();\n  REQUIRE(parser.run(\"42\") == 0x42);\n  REQUIRE(parser.run(\"FA34ABC\") == 0xFA34ABC);\n}\n\nTEST_CASE(\"Character Program\") {\n  auto program = presets::createCharacterProgram();\n  REQUIRE(program.run(\"a\") == 'a');\n  REQUIRE(program.run(\"5\") == '5');\n  REQUIRE(program.run(\"\\\\\\\\\") == '\\\\');\n  REQUIRE(program.run(\"\\\\n\") == '\\n');\n  REQUIRE(program.run(\"\\\\t\") == '\\t');\n  REQUIRE(program.run(\"\\\\0\") == '\\0');\n}\n\nTEST_CASE(\"String Program\") {\n  auto [open, close]\n      = GENERATE(as<std::tuple<std::string, std::string>>(), std::make_tuple(\"'\", \"'\"),\n                 std::make_tuple(\"``\", \"''\"), std::make_tuple(\"begin \", \" end\"));\n  auto program = presets::createStringProgram(open, close);\n  REQUIRE(program.run(open + \"Hello World!\" + close) == \"Hello World!\");\n  REQUIRE(program.run(open + \"Hello\\\\nEscaped \\\\\" + close + \"!\" + close)\n          == \"Hello\\nEscaped \" + close + \"!\");\n}\n\nTEST_CASE(\"PEG Parser\") {\n  auto rc = [](std::string_view name) {\n    return grammar::Node::Rule(grammar::makeRule(name, grammar::Node::Empty()));\n  };\n\n  auto parser = presets::createPEGProgram();\n  REQUIRE(stream_to_string(*parser.run(\"rule\", rc)) == \"rule\");\n  REQUIRE(stream_to_string(*parser.run(\"rule_2\", rc)) == \"rule_2\");\n  REQUIRE(stream_to_string(*parser.run(\"!rule\", rc)) == \"!rule\");\n  REQUIRE(stream_to_string(*parser.run(\"&rule\", rc)) == \"&rule\");\n  REQUIRE(stream_to_string(*parser.run(\"rule+\", rc)) == \"rule+\");\n  REQUIRE(stream_to_string(*parser.run(\"rule*\", rc)) == \"rule*\");\n  REQUIRE(stream_to_string(*parser.run(\"rule?\", rc)) == \"rule?\");\n  REQUIRE(stream_to_string(*parser.run(\"'word'\", rc)) == \"'word'\");\n  REQUIRE(stream_to_string(*parser.run(\"[a-z]\", rc)) == \"[a-z]\");\n  REQUIRE(stream_to_string(*parser.run(\"[abc]\", rc)) == \"('a' | 'b' | 'c')\");\n  REQUIRE(stream_to_string(*parser.run(\"[abc-de]\", rc)) == \"('a' | 'b' | [c-d] | 'e')\");\n  REQUIRE(stream_to_string(*parser.run(\"[abc\\\\-d]\", rc)) == \"('a' | 'b' | 'c' | '-' | 'd')\");\n  REQUIRE(stream_to_string(*parser.run(\"<EOF>\", rc)) == \"<EOF>\");\n  REQUIRE(parser.run(\"''\", rc)->symbol == grammar::Node::Symbol::EMPTY);\n  REQUIRE(stream_to_string(*parser.run(\"''\", rc)) == \"''\");\n  REQUIRE(parser.run(\"[]\", rc)->symbol == grammar::Node::Symbol::ERROR);\n  REQUIRE(stream_to_string(*parser.run(\"[]\", rc)) == \"[]\");\n  REQUIRE(stream_to_string(*parser.run(\".\", rc)) == \".\");\n  REQUIRE(stream_to_string(*parser.run(\"a   b  c\", rc)) == \"(a b c)\");\n  REQUIRE(stream_to_string(*parser.run(\"a   |  b |\\tc\", rc)) == \"(a | b | c)\");\n  REQUIRE(stream_to_string(*parser.run(\"'hello' | world '!'\", rc)) == \"('hello' | (world '!'))\");\n  REQUIRE(stream_to_string(*parser.run(\"('a'+ (.? | b | '')* [0-9] &<EOF>)\", rc))\n          == \"('a'+ (.? | b | '')* [0-9] &<EOF>)\");\n  REQUIRE_THROWS(parser.run(\"a | b | \", rc));\n  REQUIRE_THROWS(parser.run(\"a b @\", rc));\n  REQUIRE_THROWS(parser.run(\"42\", rc));\n}\n\nTEST_CASE(\"Program with return value\") {\n  ParserGenerator<int> program;\n  REQUIRE_THROWS_AS(program.run(\"aa\"), SyntaxError);\n  REQUIRE_THROWS_WITH(program.run(\"\"), \"syntax error at character 1 while parsing undefined\");\n  program.setRule(\"A\", \"'a'\");\n  program.setStart(program.setRule(\"B\", \"A+\"));\n  REQUIRE(program.parser.parse(\"aa\")->valid);\n  REQUIRE_THROWS_AS(program.run(\"aa\"), InterpreterError);\n  REQUIRE_THROWS_WITH(program.run(\"aa\"), \"no evaluator for rule 'A'\");\n  auto count = 0;\n  program.setRule(\"A\", \"'a'\", [&](auto) { return ++count; });\n  REQUIRE(program.run(\"aaa\") == 3);\n  REQUIRE(count == 3);\n}\n\nTEST_CASE(\"Program with argument\") {\n  ParserGenerator<void, int &> program;\n  int count = 0;\n  REQUIRE_THROWS(program.run(\"\", count));\n  REQUIRE_THROWS(program.run(\"aa\", count));\n  program.setRule(\"A\", \"'a'\");\n  program.setStart(program.setRule(\"B\", \"A+\"));\n  REQUIRE(program.parser.parse(\"aa\")->valid);\n  REQUIRE_NOTHROW(program.run(\"aa\", count));\n  program.setRule(\"A\", \"'a'\", [&](auto, int &count) { ++count; });\n  REQUIRE_NOTHROW(program.run(\"aaa\", count));\n  REQUIRE(count == 3);\n}\n\nTEST_CASE(\"Evaluation\") {\n  ParserGenerator<int> numberProgram;\n  numberProgram.setStart(numberProgram.setRule(\"Number\", \"'-'? [0-9] [0-9]*\",\n                                               [](auto e) { return std::stoi(e.string()); }));\n  REQUIRE(numberProgram.run(\"3\") == 3);\n  REQUIRE(numberProgram.run(\"-42\") == -42);\n\n  ParserGenerator<float> calculator;\n  calculator.setSeparatorRule(\"Whitespace\", \"[\\t ]\");\n  calculator.setStart(calculator.setRule(\"Expression\", \"Sum\"));\n  calculator.setRule(\"Sum\", \"Product ('+' Product)*\", [](auto e) {\n    float res = 0;\n    for (auto t : e) {\n      res += t.evaluate();\n    }\n    return res;\n  });\n  calculator.setRule(\"Product\", \"Number ('*' Number)*\", [](auto e) {\n    float res = 1;\n    for (auto t : e) {\n      res *= t.evaluate();\n    }\n    return res;\n  });\n  calculator.setProgramRule(\"Number\", numberProgram);\n  REQUIRE(calculator.run(\"42\") == 42);\n  REQUIRE(calculator.run(\"1+2\") == 3);\n  REQUIRE(calculator.run(\"2 * 3\") == 6);\n  REQUIRE(calculator.run(\"1 + 2*3\") == 7);\n  REQUIRE(calculator.run(\"  1 + 2*3*1 +4 * 5  \") == 27);\n  REQUIRE_THROWS(calculator.run(\"1+2*\"));\n}\n#include <iostream>\nTEST_CASE(\"Left recursion\") {\n  ParserGenerator<float> calculator;\n  calculator.setSeparatorRule(\"Whitespace\", \"[\\t ]\");\n  calculator.setStart(calculator.setRule(\"Expression\", \"Sum | Atomic\"));\n  calculator.setRule(\"Sum\", \"Addition | Product\");\n  calculator.setRule(\"NegativeSummand\", \"'-' Product\", [](auto e) { return -e[0].evaluate(); });\n  calculator.setRule(\"Addition\", \"Sum ('+' Product | NegativeSummand)\",\n                     [](auto e) { return e[0].evaluate() + e[1].evaluate(); });\n  calculator.setRule(\"Product\", \"Multiplication | Atomic\");\n  calculator.setRule(\"Multiplication\", \"Product '*' Atomic\",\n                     [](auto e) { return e[0].evaluate() * e[1].evaluate(); });\n  calculator.setRule(\"Atomic\", \"Number | Negative | Brackets\");\n  calculator.setProgramRule(\"Number\", presets::createFloatProgram());\n  calculator.setRule(\"Negative\", \"'-' Atomic\", [](auto e) { return -e[0].evaluate(); });\n  calculator.setRule(\"Brackets\", \"'(' Expression ')'\");\n\n  REQUIRE(calculator.run(\"42\") == 42);\n  REQUIRE(calculator.run(\"1+2\") == 3);\n  REQUIRE(calculator.run(\"1+2-3-5\") == -5);\n  REQUIRE(calculator.run(\"2 * 3\") == 6);\n  REQUIRE(calculator.run(\"1 + 2*3\") == 7);\n  REQUIRE(calculator.run(\"  1 + 2*3*1 +4 * 5  \") == 27);\n\n  REQUIRE(calculator.run(\"-42\") == -42);\n  REQUIRE(calculator.run(\"--42\") == 42);\n  REQUIRE(calculator.run(\"---42\") == -42);\n  REQUIRE(calculator.run(\"----------------------------------------------------42\") == 42);\n\n  REQUIRE_THROWS(calculator.run(\"1+2*\"));\n}\n\nTEST_CASE(\"Filter\") {\n  ParserGenerator<> program;\n  program.setStart(program.setFilteredRule(\"B\", \"A+\", [](auto tree) {\n    REQUIRE_THAT(tree->string(), Catch::Matchers::Matches(\"a+\"));\n    return tree->inner.size() % 3 == 0;\n  }));\n  program.setRule(\"A\", \"'a'\");\n  auto N = GENERATE(range<size_t>(1, 10));\n  REQUIRE(program.parse(std::string(N, 'a'))->valid == (N % 3 == 0));\n}\n\nTEST_CASE(\"Broken Grammar\") {\n  SECTION(\"Wrong type\") {\n    auto node = grammar::Node::Range('1', '9');\n    node->data = std::string(\"nope\");\n    auto rule = makeRule(\"Invalid\", node);\n    REQUIRE_THROWS_WITH(Parser::parse(\"1\", rule), \"corrupted grammar node\");\n  }\n  SECTION(\"Illegal type\") {\n    auto node = grammar::Node::Any();\n    node->symbol = grammar::Node::Symbol(-1);\n    auto rule = makeRule(\"Invalid\", node);\n    REQUIRE_THROWS_WITH(Parser::parse(\"\", rule), Catch::Matchers::Contains(\"UNKNOWN_SYMBOL\"));\n  }\n  SECTION(\"Deleted Rule\") {\n    auto deletedRule = makeRule(\"deletedRule\", grammar::Node::Any());\n    auto rule = makeRule(\"Rule\", grammar::Node::WeakRule(deletedRule));\n    REQUIRE_NOTHROW(Parser::parse(\"x\", rule));\n    deletedRule.reset();\n    REQUIRE_THROWS_AS(Parser::parse(\"x\", rule), Parser::GrammarError);\n    REQUIRE_THROWS_WITH(Parser::parse(\"x\", rule), Catch::Matchers::Contains(\"<DeletedRule>\"));\n  }\n}\n\nTEST_CASE(\"Syntax Tree\") {\n  ParserGenerator<> program;\n  program.setStart(program.setRule(\"B\", \"A+\"));\n  program.setRule(\"A\", \".\");\n  auto tree = program.parse(\"abc\");\n  REQUIRE(stream_to_string(*tree) == \"B(A('a'), A('b'), A('c'))\");\n}\n\nTEST_CASE(\"C++ Operators\") {\n  ParserGenerator<std::string> program;\n\n  program[\"B\"] << \"A+\" << [](auto tree) { return tree->inner.size() % 3 == 0; } >> [](auto e) {\n    std::string res;\n    for (auto arg : e) {\n      res += arg.evaluate();\n    }\n    return res;\n  };\n\n  program[\"A\"] << \".\" >> [](auto e) { return std::string(1, e.view()[0] + 1); };\n\n  program.setStart(program[\"B\"]);\n\n  REQUIRE_THROWS(program.run(\"ab\"));\n  REQUIRE(program.run(\"abc\") == \"bcd\");\n}\n\nTEST_CASE(\"Parsing\") {\n  ParserGenerator<int> program;\n  program.setStart(program[\"A\"]);\n  program[\"A\"] << \"B (' ' A) | B\" >> [](auto e) {\n    return std::accumulate(e.begin(), e.end(), 0, [](auto a, auto b) { return a + b.evaluate(); });\n  };\n  program[\"B\"] << \"&'b' . ''\" >> [](auto) { return 1; };\n  REQUIRE_THROWS(program.run(\"a\"));\n  REQUIRE(program.run(\"b\") == 1);\n  REQUIRE(program.run(\"b b\") == 2);\n  REQUIRE(program.run(\"b b b\") == 3);\n}\n\nTEST_CASE(\"Documentation Example\") {\n  ParserGenerator<float> g;\n  g.setSeparator(g[\"Whitespace\"] << \"[\\t ]\");\n  g[\"Sum\"] << \"Add | Subtract | Product\";\n  g[\"Product\"] << \"Multiply | Divide | Atomic\";\n  g[\"Atomic\"] << \"Number | '(' Sum ')'\";\n  g[\"Add\"] << \"Sum '+' Product\" >> [](auto e) { return e[0].evaluate() + e[1].evaluate(); };\n  g[\"Subtract\"] << \"Sum '-' Product\" >> [](auto e) { return e[0].evaluate() - e[1].evaluate(); };\n  g[\"Multiply\"] << \"Product '*' Atomic\" >> [](auto e) { return e[0].evaluate() * e[1].evaluate(); };\n  g[\"Divide\"] << \"Product '/' Atomic\" >> [](auto e) { return e[0].evaluate() / e[1].evaluate(); };\n  g[\"Number\"] << \"'-'? [0-9]+ ('.' [0-9]+)?\" >> [](auto e) { return stof(e.string()); };\n  g.setStart(g[\"Sum\"]);\n\n  REQUIRE(g.run(\"42\") == Approx(42));\n  REQUIRE(g.run(\"2+3\") == Approx(5));\n  REQUIRE(g.run(\"2*3\") == Approx(6));\n  REQUIRE(g.run(\"1+2+3\") == Approx(6));\n  REQUIRE(g.run(\"1+2*3\") == Approx(7));\n  REQUIRE(g.run(\"1+2-3\") == Approx(0));\n  REQUIRE(g.run(\"2*2/4*3\") == Approx(3));\n  REQUIRE(g.run(\"1 - 2*3/2 + 4\") == Approx(2));\n  REQUIRE(g.run(\"1 + 2 * (3+4)/ 2 - 3\") == Approx(5));\n}\n\nTEST_CASE(\"Subscript Operators\") {\n  ParserGenerator<bool> program;\n  program[\"Word\"] << \"[a-z]+\";\n  program[\"Yell\"] << \"[A-Z]+\";\n  program[\"Start\"] << \"Word | Yell\" >> [](auto e) { return bool(e[\"Yell\"]); };\n  program.setStart(program[\"Start\"]);\n\n  REQUIRE(!program.run(\"hello\"));\n  REQUIRE(program.run(\"HELLO\"));\n}\n"
  }
]