diff --git a/packages/PEGTL/.appveyor.yml b/packages/PEGTL/.appveyor.yml
index 716434a3043a74f22d63bcb374dac3b7a9c7766d..02289836c6090f6ceccf750a79ed6a2e875082c5 100644
--- a/packages/PEGTL/.appveyor.yml
+++ b/packages/PEGTL/.appveyor.yml
@@ -1,65 +1,29 @@
 version: '{branch}-{build}'
 
-os:
-  - Visual Studio 2017
-
 skip_commits:
   files:
+    - README.md
     - doc/**/*
 
-environment:
-  matrix:
-    - GENERATOR: Visual Studio 15 2017
-      platform: x86
-      configuration: Debug
+os:
+  - Visual Studio 2017
 
-    - GENERATOR: Visual Studio 15 2017
-      platform: x86
-      configuration: Release
+platform:
+  - x86
+  - x64
 
-    - GENERATOR: Visual Studio 15 2017
-      platform: x64
-      configuration: Debug
+configuration:
+  - Debug
+  - Release
 
+environment:
+  matrix:
     - GENERATOR: Visual Studio 15 2017
-      platform: x64
-      configuration: Release
-
-    - GENERATOR: Visual Studio 15 2017 Win64
-      platform: x86
-      configuration: Debug
 
     - GENERATOR: Visual Studio 15 2017 Win64
-      platform: x86
-      configuration: Release
-
-    - GENERATOR: Visual Studio 15 2017 Win64
-      platform: x64
-      configuration: Debug
-
-    - GENERATOR: Visual Studio 15 2017 Win64
-      platform: x64
-      configuration: Release
-
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-      GENERATOR: Visual Studio 16 2019
-      platform: x86
-      configuration: Debug
 
     - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
       GENERATOR: Visual Studio 16 2019
-      platform: x86
-      configuration: Release
-
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-      GENERATOR: Visual Studio 16 2019
-      platform: x64
-      configuration: Debug
-
-    - APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
-      GENERATOR: Visual Studio 16 2019
-      platform: x64
-      configuration: Release
 
 init: []
 
@@ -74,4 +38,3 @@ build_script:
 
 test_script:
 - ctest -C "%CONFIGURATION%" --output-on-failure
-
diff --git a/packages/PEGTL/.clang-format b/packages/PEGTL/.clang-format
index 729f882aad10760bbc2afa0fe571376390a19bdb..c423ca2749e808e112ba69db6d6f33ee76ca955f 100644
--- a/packages/PEGTL/.clang-format
+++ b/packages/PEGTL/.clang-format
@@ -3,7 +3,7 @@
 # clang-format -i -style=file $(find . -name '[^.]*.[hc]pp')
 
 Language: Cpp
-Standard: Cpp11
+Standard: Latest
 
 AccessModifierOffset: -3
 AlignAfterOpenBracket: Align
diff --git a/packages/PEGTL/.clang-tidy b/packages/PEGTL/.clang-tidy
index 14eb1216c414dfe3269d2b3195855173b18299f1..2af75f8edbfac208c7fef1dfcbbcb1da1093f367 100644
--- a/packages/PEGTL/.clang-tidy
+++ b/packages/PEGTL/.clang-tidy
@@ -21,6 +21,7 @@ Checks: >-
   readability-*,
   -readability-avoid-const-params-in-decls,
   -readability-magic-numbers,
+  -readability-static-accessed-through-instance,
 
 CheckOptions:
   - { key: readability-identifier-naming.ClassCase,     value: lower_case }
diff --git a/packages/PEGTL/.gitrepo b/packages/PEGTL/.gitrepo
index 7eea19117c858927ae7c942b0fe1631d3c411044..00b4dcf367034ec66b746e464a2f7bb9ffdcf080 100644
--- a/packages/PEGTL/.gitrepo
+++ b/packages/PEGTL/.gitrepo
@@ -6,7 +6,7 @@
 [subrepo]
 	remote = git@github.com:taocpp/PEGTL.git
 	branch = master
-	commit = 1a4a2d0df43f773893e0e4102d56d85e6b3fb7fc
-	parent = 28dff7b443629a23205fd32909491e7e5de495a3
+	commit = 3d4fee2837decb5ecff9543276c4aa922b280972
+	parent = 0a259f7e3e4fe2364b8d45b641c7f48ff3bc7341
 	cmdver = 0.4.1
 	method = merge
diff --git a/packages/PEGTL/.travis.yml b/packages/PEGTL/.travis.yml
index 95e20d5032f8e6f5e3cd3adb8f7d200153d039a8..8d81bb3ce3272882de4fc246274b13ea56acfb72 100644
--- a/packages/PEGTL/.travis.yml
+++ b/packages/PEGTL/.travis.yml
@@ -4,43 +4,48 @@ dist: xenial
 
 jobs:
   include:
-    - &gcc-7
-      compiler: gcc
+    - compiler: gcc
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test ]
-          packages: [ g++-7 ]
+          packages: [ g++-8 ]
       env:
-        - CXX=g++-7
+        - CXX=g++-8
+        - LDFLAGS="-lstdc++fs"
 
-    - &gcc-8
-      compiler: gcc
+    - compiler: gcc
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test ]
-          packages: [ g++-8 ]
-      env:
-        - CXX=g++-8
-
-    - <<: *gcc-8
+          packages: [ g++-9 ]
       env:
-        - CXX=g++-8
-        - CPPFLAGS="-fno-rtti"
+        - CXX=g++-9
 
-    - &gcc-9
+    - &gcc-10
+      dist: bionic
       compiler: gcc
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test ]
-          packages: [ g++-9 ]
+          packages: [ g++-10 ]
       env:
-        - CXX=g++-9
+        - CXX=g++-10
+
+    - <<: *gcc-10
+      env:
+        - CXX=g++-10
+        - CXXSTD="-std=c++20"
+
+    - <<: *gcc-10
+      env:
+        - CXX=g++-10
+        - CPPFLAGS="-fno-rtti"
 
     - compiler: clang
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test, llvm-toolchain-xenial-5 ]
-          packages: [ clang-5.0, g++-7 ]
+          packages: [ clang-5.0, g++-9 ]
       env:
         - CXX=clang++-5.0
 
@@ -48,7 +53,7 @@ jobs:
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test, llvm-toolchain-xenial-6 ]
-          packages: [ clang-6.0, g++-7 ]
+          packages: [ clang-6.0, g++-9 ]
       env:
         - CXX=clang++-6.0
 
@@ -56,7 +61,7 @@ jobs:
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test, llvm-toolchain-xenial-7 ]
-          packages: [ clang-7, g++-7 ]
+          packages: [ clang-7, g++-9 ]
       env:
         - CXX=clang++-7
 
@@ -64,14 +69,14 @@ jobs:
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test, llvm-toolchain-xenial-8 ]
-          packages: [ clang-8, g++-8 ]
+          packages: [ clang-8, g++-9 ]
       env:
         - CXX=clang++-8
 
     - compiler: clang
       addons:
         apt:
-          sources: &clang-9-sources
+          sources:
             - ubuntu-toolchain-r-test
             - sourceline: deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main
               key_url: https://apt.llvm.org/llvm-snapshot.gpg.key
@@ -80,17 +85,23 @@ jobs:
         - CXX=clang++-9
 
     - &clang-10
+      dist: bionic
       compiler: clang
       addons:
         apt:
           sources: &clang-10-sources
             - ubuntu-toolchain-r-test
-            - sourceline: deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-10 main
+            - sourceline: deb https://apt.llvm.org/bionic/ llvm-toolchain-bionic-10 main
               key_url: https://apt.llvm.org/llvm-snapshot.gpg.key
-          packages: [ clang-10, g++-9 ]
+          packages: [ clang-10, g++-10 ]
       env:
         - CXX=clang++-10
 
+    - <<: *clang-10
+      env:
+        - CXX=clang++-10
+        - CXXSTD="-std=c++20"
+
     - <<: *clang-10
       env:
         - CXX=clang++-10
@@ -101,19 +112,12 @@ jobs:
         - CXX=clang++-10
         - CPPFLAGS="-fno-rtti"
 
-    - &osx
-      os: osx
-      osx_image: xcode9.4
+    - os: osx
+      osx_image: xcode11.4
       compiler: clang
       env:
         - CXX=clang++
 
-    - <<: *osx
-      osx_image: xcode10.3
-
-    - <<: *osx
-      osx_image: xcode11.4
-
     - &android
       compiler: clang
       addons:
@@ -153,14 +157,14 @@ jobs:
         - ANDROID_ABI=arm64-v8a
         - ANDROID_PLATFORM=android-24
 
-    - <<: *gcc-9
+    - <<: *gcc-10
       env:
-        - CXX=g++-9
+        - CXX=g++-10
         - CPPFLAGS="-fsanitize=undefined -fuse-ld=gold"
 
-    - <<: *gcc-9
+    - <<: *gcc-10
       env:
-        - CXX=g++-9
+        - CXX=g++-10
         - CPPFLAGS="-fsanitize=address -fuse-ld=gold"
 
     - <<: *clang-10
@@ -173,53 +177,57 @@ jobs:
         - CXX=clang++-10
         - CPPFLAGS="-fsanitize=address"
 
-    - compiler: clang
+    - dist: bionic
+      compiler: clang
       addons:
         apt:
           sources: *clang-10-sources
-          packages: [ clang-10, clang-tidy-10, g++-9 ]
+          packages: [ clang-10, clang-tidy-10, g++-10 ]
       env:
         - CXX=clang++-10
         - CLANG_TIDY=clang-tidy-10
       script:
         - make -kj3 clang-tidy
 
-    - compiler: clang
+    - dist: bionic
+      compiler: clang
       addons:
         apt:
           sources: *clang-10-sources
-          packages: [ clang-tools-10, g++-9 ]
+          packages: [ clang-tools-10, g++-10 ]
       script:
         - scan-build-10 --use-c++=clang++-10 --status-bugs make -kj3
 
-    - compiler: gcc
+    - dist: bionic
+      compiler: gcc
       addons:
         apt:
           sources: [ ubuntu-toolchain-r-test ]
-          packages: [ g++-9, valgrind ]
+          packages: [ g++-10, valgrind ]
       env:
-        - CXX=g++-9
+        - CXX=g++-10
         - SPECIAL=valgrind
       script:
         - make -kj3 valgrind
 
-    - <<: *gcc-7
+    - <<: *gcc-10
       env:
-        - CXX=g++-7
+        - CXX=g++-10
         - CXXFLAGS="-O0 --coverage"
       before_script:
         - pip install --user cpp-coveralls
       script:
         - make -kj3 check
-        - coveralls --gcov gcov-7 --gcov-options '\-lp' --exclude src
+        - coveralls --gcov gcov-10 --gcov-options '\-lp' --exclude src
 
-    - compiler: clang
+    - dist: bionic
+      compiler: clang
       addons:
         apt:
-          sources: *clang-9-sources
-          packages: [ clang-format-9, g++-9 ]
+          sources: *clang-10-sources
+          packages: [ clang-format-10, g++-10 ]
       script:
-        - clang-format-9 -i -style=file $(find . -name '[^.]*.[hc]pp')
+        - clang-format-10 -i -style=file $(find . -name '[^.]*.[hc]pp')
         - git diff --exit-code
 
 script:
diff --git a/packages/PEGTL/Makefile b/packages/PEGTL/Makefile
index 9ff538aff11cec39c905b5d76871e6338af76b73..3f47845535131816144829cf7cbecf1724fbe640 100644
--- a/packages/PEGTL/Makefile
+++ b/packages/PEGTL/Makefile
@@ -62,7 +62,7 @@ valgrind: $(UNIT_TESTS:%=%.valgrind)
 	@echo "All $(words $(UNIT_TESTS)) valgrind tests passed."
 
 build/%.clang-tidy: % .clang-tidy
-	$(CLANG_TIDY) -quiet $< -- $(CXXSTD) -Iinclude $(CPPFLAGS) $(CXXFLAGS) 2>/dev/null
+	$(CLANG_TIDY) -quiet $< -- $(CXXSTD) -Iinclude $(CPPFLAGS) $(CXXFLAGS) $(LDFLAGS) 2>/dev/null
 	@mkdir -p $(@D)
 	@touch $@
 
@@ -80,7 +80,7 @@ build/%.d: %.cpp Makefile
 	$(CXX) $(CXXSTD) -Iinclude $(CPPFLAGS) -MM -MQ $@ $< -o $@
 
 build/%: %.cpp build/%.d
-	$(CXX) $(CXXSTD) -Iinclude $(CPPFLAGS) $(CXXFLAGS) $< -o $@
+	$(CXX) $(CXXSTD) -Iinclude $(CPPFLAGS) $(CXXFLAGS) $< $(LDFLAGS) -o $@
 
 .PHONY: amalgamate
 amalgamate: build/amalgamated/pegtl.hpp
@@ -94,7 +94,6 @@ build/amalgamated/pegtl.hpp: $(HEADERS)
 	@sed -i -e 's%^//#include "%#include "%g' $$(find build/include -name '*.hpp')
 	@for i in $$(find build/include -name '*.hpp'); do echo "#pragma once" >tmp.out; echo "#line 1" >>tmp.out; cat $$i >>tmp.out; mv tmp.out $$i; done
 	@echo '#include "tao/pegtl.hpp"' >build/include/amalgamated.hpp
-	@echo '#include "tao/pegtl/analyze.hpp"' >>build/include/amalgamated.hpp
 	@( cd build/include ; for i in tao/pegtl/contrib/*.hpp; do echo "#include \"$$i\""; done ) >>build/include/amalgamated.hpp
 	@echo -e "/*\n\nWelcome to the Parsing Expression Grammar Template Library (PEGTL)." >$@
 	@echo -e "See https://github.com/taocpp/PEGTL/ for more information, documentation, etc.\n" >>$@
diff --git a/packages/PEGTL/README.md b/packages/PEGTL/README.md
index 2b57955028ddacc5d5418558f98a2589c1fa6680..c11b0c8822e7f67f8280f0a17529ed6344b6b498 100644
--- a/packages/PEGTL/README.md
+++ b/packages/PEGTL/README.md
@@ -1,12 +1,5 @@
 # Welcome to the PEGTL
 
-[![Release](https://img.shields.io/github/release/taocpp/PEGTL.svg)](https://github.com/taocpp/PEGTL/releases/latest)
-[![Download](https://api.bintray.com/packages/conan/conan-center/taocpp-pegtl%3A_/images/download.svg)](https://bintray.com/conan/conan-center/taocpp-pegtl%3A_/_latestVersion)
-[![TravisCI](https://travis-ci.org/taocpp/PEGTL.svg?branch=master)](https://travis-ci.org/taocpp/PEGTL)
-[![AppVeyor](https://ci.appveyor.com/api/projects/status/pa5sbnw68tu650aq/branch/master?svg=true)](https://ci.appveyor.com/project/taocpp/PEGTL)
-[![Coverage](https://coveralls.io/repos/github/taocpp/PEGTL/badge.svg?branch=master)](https://coveralls.io/github/taocpp/PEGTL)
-[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/taocpp/PEGTL.svg)](https://lgtm.com/projects/g/taocpp/PEGTL/context:cpp)
-
 The Parsing Expression Grammar Template Library (PEGTL) is a zero-dependency C++ header-only parser combinator library for creating parsers according to a [Parsing Expression Grammar](http://en.wikipedia.org/wiki/Parsing_expression_grammar) (PEG).
 
 ## Documentation
@@ -56,6 +49,9 @@ The rules are expressed in C++ as template instantiations, and it is the compile
 
 ## Status
 
+[![TravisCI](https://travis-ci.org/taocpp/PEGTL.svg?branch=master)](https://travis-ci.org/taocpp/PEGTL)
+[![AppVeyor](https://ci.appveyor.com/api/projects/status/pa5sbnw68tu650aq/branch/master?svg=true)](https://ci.appveyor.com/project/taocpp/PEGTL)
+
 Each commit is automatically tested with multiple architectures, operating systems, compilers, and versions thereof.
 
 * Windows
@@ -65,17 +61,36 @@ Each commit is automatically tested with multiple architectures, operating syste
 
 * macOS (using libc++)
 
-  * macOS 10.13, Xcode 9.4
-  * macOS 10.14, Xcode 10.3
-  * macOS 10.14, Xcode 11.4
+  * macOS 10.15, Xcode 11.4
 
 * Ubuntu 16.04 LTS (using libstdc++)
 
-  * GCC 7.x, 8.x, 9.x
-  * Clang 5.x, 6.x, 7.x, 8.x, 9.x, 10.x
+  * GCC 8.x, 9.x
+  * Clang 5.x, 6.x, 7.x, 8.x, 9.x
+
+* Ubuntu 18.04 LTS (using libstdc++)
+
+  * GCC 10.x
+  * Clang 10.x
+
+### Code Quality
+
+[![Language grade: C/C++](https://img.shields.io/lgtm/grade/cpp/g/taocpp/PEGTL.svg)](https://lgtm.com/projects/g/taocpp/PEGTL/context:cpp)
+
+Each commit is checked with Clang's [Static Analyzer](https://clang-analyzer.llvm.org/), GCC's and Clang's [sanitizers](https://github.com/google/sanitizers), [`clang-tidy`](http://clang.llvm.org/extra/clang-tidy/), and [`valgrind`](http://valgrind.org/).
+Additionally, we use [LGTM](https://lgtm.com/) to scan for (security) issues.
+Note that [LGTM](https://lgtm.com/) sometimes generate false positives, hence the above badge may not show an accurate grade.
+
+### Code Coverage
+
+[![Coverage](https://coveralls.io/repos/github/taocpp/PEGTL/badge.svg?branch=master)](https://coveralls.io/github/taocpp/PEGTL)
 
-Additionally, each commit is checked with Clang's [Static Analyzer](https://clang-analyzer.llvm.org/), GCC's and Clang's [sanitizers](https://github.com/google/sanitizers), [`clang-tidy`](http://clang.llvm.org/extra/clang-tidy/), and [`valgrind`](http://valgrind.org/).
 Code coverage is automatically measured and the unit tests cover 100% of the core library code (for releases).
+Note that the infrastructure is partially broken, hence the above badge may not show 100%.
+
+### Versioning
+
+[![Release](https://img.shields.io/github/release/taocpp/PEGTL.svg)](https://github.com/taocpp/PEGTL/releases/latest)
 
 [Releases](https://github.com/taocpp/PEGTL/releases) are done in accordance with [Semantic Versioning](http://semver.org/).
 Incompatible API changes are *only* allowed to occur between major versions.
@@ -85,47 +100,48 @@ For details see the [changelog](doc/Changelog.md).
 
 In appreciation of all contributions here are the people that have [directly contributed](https://github.com/taocpp/PEGTL/graphs/contributors) to the PEGTL and/or its development.
 
-[<img alt="andoma" src="https://avatars.githubusercontent.com/u/216384?s=117" width="117">](https://github.com/andoma)
-[<img alt="bjoe" src="https://avatars.githubusercontent.com/u/727911?s=117" width="117">](https://github.com/bjoe)
-[<img alt="bwagner" src="https://avatars.githubusercontent.com/u/447049?s=117" width="117">](https://github.com/bwagner)
-[<img alt="cdiggins" src="https://avatars.githubusercontent.com/u/1759994?s=460&v=4?s=117" width="117">](https://github.com/cdiggins)
-[<img alt="delpinux" src="https://avatars.githubusercontent.com/u/35096584?s=117" width="117">](https://github.com/delpinux)
-[<img alt="dkopecek" src="https://avatars.githubusercontent.com/u/1353140?s=117" width="117">](https://github.com/dkopecek)
-[<img alt="irrequietus" src="https://avatars.githubusercontent.com/u/231192?s=117" width="117">](https://github.com/irrequietus)
-[<img alt="jedelbo" src="https://avatars.githubusercontent.com/u/572755?s=117" width="117">](https://github.com/jedelbo)
-[<img alt="joelfrederico" src="https://avatars.githubusercontent.com/u/458871?s=117" width="117">](https://github.com/joelfrederico)
-[<img alt="johelegp" src="https://avatars.githubusercontent.com/u/21071787?s=117" width="117">](https://github.com/johelegp)
-[<img alt="jovermann" src="https://avatars.githubusercontent.com/u/6087443?s=117" width="117">](https://github.com/jovermann)
-[<img alt="kneth" src="https://avatars.githubusercontent.com/u/1225363?s=117" width="117">](https://github.com/kneth)
-[<img alt="kuzmas" src="https://avatars.githubusercontent.com/u/1858553?s=117" width="117">](https://github.com/kuzmas)
-[<img alt="lambdafu" src="https://avatars.githubusercontent.com/u/1138455?s=117" width="117">](https://github.com/lambdafu)
-[<img alt="lichray" src="https://avatars.githubusercontent.com/u/433009?s=117" width="117">](https://github.com/lichray)
-[<img alt="michael-brade" src="https://avatars.githubusercontent.com/u/8768950?s=117" width="117">](https://github.com/michael-brade)
-[<img alt="mkrupcale" src="https://avatars.githubusercontent.com/u/13936020?s=117" width="117">](https://github.com/mkrupcale)
-[<img alt="newproggie" src="https://avatars.githubusercontent.com/u/162319?s=460&v=4?s=117" width="117">](https://github.com/newproggie)
-[<img alt="obiwahn" src="https://avatars.githubusercontent.com/u/741109?s=117" width="117">](https://github.com/obiwahn)
-[<img alt="ohanar" src="https://avatars.githubusercontent.com/u/1442822?s=117" width="117">](https://github.com/ohanar)
-[<img alt="pauloscustodio" src="https://avatars.githubusercontent.com/u/70773?s=117" width="117">](https://github.com/pauloscustodio)
-[<img alt="pleroux0" src="https://avatars.githubusercontent.com/u/39619854?s=117" width="117">](https://github.com/pleroux0)
-[<img alt="quadfault" src="https://avatars.githubusercontent.com/u/30195320?s=117" width="117">](https://github.com/quadfault)
-[<img alt="robertcampion" src="https://avatars.githubusercontent.com/u/4220569?s=117" width="117">](https://github.com/robertcampion)
-[<img alt="samhocevar" src="https://avatars.githubusercontent.com/u/245089?s=117" width="117">](https://github.com/samhocevar)
-[<img alt="sanssecours" src="https://avatars.githubusercontent.com/u/691989?s=117" width="117">](https://github.com/sanssecours)
-[<img alt="sgbeal" src="https://avatars.githubusercontent.com/u/235303?s=117" width="117">](https://github.com/sgbeal)
-[<img alt="skyrich62" src="https://avatars.githubusercontent.com/u/23705081?s=117" width="117">](https://github.com/skyrich62)
-[<img alt="studoot" src="https://avatars.githubusercontent.com/u/799344?s=117" width="117">](https://github.com/studoot)
-[<img alt="svenjo" src="https://avatars.githubusercontent.com/u/1538181?s=460&v=4?s=117" width="117">](https://github.com/svenjo)
-[<img alt="wickedmic" src="https://avatars.githubusercontent.com/u/12001183?s=117" width="117">](https://github.com/wickedmic)
-[<img alt="wravery" src="https://avatars.githubusercontent.com/u/6502881?s=117" width="117">](https://github.com/wravery)
-[<img alt="zhihaoy" src="https://avatars.githubusercontent.com/u/43971430?s=117" width="117">](https://github.com/zhihaoy)
+[<img alt="andoma" src="https://avatars.githubusercontent.com/u/216384" width="120">](https://github.com/andoma)
+[<img alt="bjoe" src="https://avatars.githubusercontent.com/u/727911" width="120">](https://github.com/bjoe)
+[<img alt="bwagner" src="https://avatars.githubusercontent.com/u/447049" width="120">](https://github.com/bwagner)
+[<img alt="cdiggins" src="https://avatars.githubusercontent.com/u/1759994" width="120">](https://github.com/cdiggins)
+[<img alt="delpinux" src="https://avatars.githubusercontent.com/u/35096584" width="120">](https://github.com/delpinux)
+[<img alt="dkopecek" src="https://avatars.githubusercontent.com/u/1353140" width="120">](https://github.com/dkopecek)
+[<img alt="gene-hightower" src="https://avatars.githubusercontent.com/u/3957811" width="120">](https://github.com/gene-hightower)
+[<img alt="irrequietus" src="https://avatars.githubusercontent.com/u/231192" width="120">](https://github.com/irrequietus)
+[<img alt="jedelbo" src="https://avatars.githubusercontent.com/u/572755" width="120">](https://github.com/jedelbo)
+[<img alt="joelfrederico" src="https://avatars.githubusercontent.com/u/458871" width="120">](https://github.com/joelfrederico)
+[<img alt="johelegp" src="https://avatars.githubusercontent.com/u/21071787" width="120">](https://github.com/johelegp)
+[<img alt="jovermann" src="https://avatars.githubusercontent.com/u/6087443" width="120">](https://github.com/jovermann)
+[<img alt="kneth" src="https://avatars.githubusercontent.com/u/1225363" width="120">](https://github.com/kneth)
+[<img alt="kuzmas" src="https://avatars.githubusercontent.com/u/1858553" width="120">](https://github.com/kuzmas)
+[<img alt="lambdafu" src="https://avatars.githubusercontent.com/u/1138455" width="120">](https://github.com/lambdafu)
+[<img alt="lichray" src="https://avatars.githubusercontent.com/u/433009" width="120">](https://github.com/lichray)
+[<img alt="michael-brade" src="https://avatars.githubusercontent.com/u/8768950" width="120">](https://github.com/michael-brade)
+[<img alt="mkrupcale" src="https://avatars.githubusercontent.com/u/13936020" width="120">](https://github.com/mkrupcale)
+[<img alt="newproggie" src="https://avatars.githubusercontent.com/u/162319" width="120">](https://github.com/newproggie)
+[<img alt="obiwahn" src="https://avatars.githubusercontent.com/u/741109" width="120">](https://github.com/obiwahn)
+[<img alt="ohanar" src="https://avatars.githubusercontent.com/u/1442822" width="120">](https://github.com/ohanar)
+[<img alt="pauloscustodio" src="https://avatars.githubusercontent.com/u/70773" width="120">](https://github.com/pauloscustodio)
+[<img alt="pleroux0" src="https://avatars.githubusercontent.com/u/39619854" width="120">](https://github.com/pleroux0)
+[<img alt="quadfault" src="https://avatars.githubusercontent.com/u/30195320" width="120">](https://github.com/quadfault)
+[<img alt="robertcampion" src="https://avatars.githubusercontent.com/u/4220569" width="120">](https://github.com/robertcampion)
+[<img alt="samhocevar" src="https://avatars.githubusercontent.com/u/245089" width="120">](https://github.com/samhocevar)
+[<img alt="sanssecours" src="https://avatars.githubusercontent.com/u/691989" width="120">](https://github.com/sanssecours)
+[<img alt="sgbeal" src="https://avatars.githubusercontent.com/u/235303" width="120">](https://github.com/sgbeal)
+[<img alt="skyrich62" src="https://avatars.githubusercontent.com/u/23705081" width="120">](https://github.com/skyrich62)
+[<img alt="studoot" src="https://avatars.githubusercontent.com/u/799344" width="120">](https://github.com/studoot)
+[<img alt="svenjo" src="https://avatars.githubusercontent.com/u/1538181" width="120">](https://github.com/svenjo)
+[<img alt="wickedmic" src="https://avatars.githubusercontent.com/u/12001183" width="120">](https://github.com/wickedmic)
+[<img alt="wravery" src="https://avatars.githubusercontent.com/u/6502881" width="120">](https://github.com/wravery)
+[<img alt="zhihaoy" src="https://avatars.githubusercontent.com/u/43971430" width="120">](https://github.com/zhihaoy)
 
 ## The Art of C++
 
 The PEGTL is part of [The Art of C++](https://taocpp.github.io/).
 
-[<img alt="colinh" src="https://avatars.githubusercontent.com/u/113184?s=117" width="117">](https://github.com/colinh)
-[<img alt="d-frey" src="https://avatars.githubusercontent.com/u/3956325?s=117" width="117">](https://github.com/d-frey)
-[<img alt="uilianries" src="https://avatars.githubusercontent.com/u/4870173?s=117" width="117">](https://github.com/uilianries)
+[<img alt="colinh" src="https://avatars.githubusercontent.com/u/113184" width="120">](https://github.com/colinh)
+[<img alt="d-frey" src="https://avatars.githubusercontent.com/u/3956325" width="120">](https://github.com/d-frey)
+[<img alt="uilianries" src="https://avatars.githubusercontent.com/u/4870173" width="120">](https://github.com/uilianries)
 
 ## Contact
 
diff --git a/packages/PEGTL/doc/Actions-and-States.md b/packages/PEGTL/doc/Actions-and-States.md
index ae234dc28007583c315f81b06205a7198dfb8dc1..480df8140fae8188ddad1c9f2271b99ce21225f0 100644
--- a/packages/PEGTL/doc/Actions-and-States.md
+++ b/packages/PEGTL/doc/Actions-and-States.md
@@ -198,7 +198,7 @@ Conversely `iterator()` returns a pointer or iterator to the beginning of the ac
 More importantly the `action_input` does **not** own the data it points to, it belongs to the original input used in the parsing run.
 Therefore **the validity of the pointed-to data might not extend (much) beyond the call to `apply()`**!
 
-When the original input has tracking mode `eager`, the `iterator_t` returned by `action_input::iterator()` will contain the `byte`, `line` and `byte_in_line` counters corresponding to the beginning of the matched input represented by the `action_input`.
+When the original input has tracking mode `eager`, the `iterator_t` returned by `action_input::iterator()` will contain the `byte`, `line` and `column` counters corresponding to the beginning of the matched input represented by the `action_input`.
 
 When the original input has tracking mode `lazy`, then `action_input::position()` is not efficient because it calculates the line number etc. by scanning the complete original input from the beginning
 
diff --git a/packages/PEGTL/doc/Changelog.md b/packages/PEGTL/doc/Changelog.md
index 3198b38dfad39a10d59ff81600ee256c3b0ded67..a6290b6b79212f37de6db731df3879cf0646d58e 100644
--- a/packages/PEGTL/doc/Changelog.md
+++ b/packages/PEGTL/doc/Changelog.md
@@ -5,36 +5,51 @@
 **Not yet released**
 
 * Use the [**migration guide**](Migration-Guide.md#version-300) when updating.
-* Updated required C++ standard to C++17.
-* Updated required [CMake](https://cmake.org/) version to 3.8.
-* The macro `TAO_PEGTL_NAMESPACE` now contains the fully qualified namespace, e.g. `tao::pegtl`.
-* Replaced `tao::pegtl::input_error` with `std::system_error`.
-* Moved the analysis function and header to contrib.
-* Replaced `analysis_t` with more general and complete `rule_t` and `subs_t`.
-* Added functions to visit all rules of a grammar.
-* Added infrastructure and functions to measure rule coverage of a parsing run.
-* Added [`must_if<>`](Errors-and-Exceptions.md#custom-exception-messages)
-  * Allows to define custom error messages for global errors.
-  * As a non-intrusive way to define global parse errors for a grammar retroactively.
-* Moved rule `eolf` from inline namespace `tao::pegtl::ascii` to `tao::pegtl`.
-* Changed message of `tao::pegtl::parse_error` to no longer contain the position redundantly.
-* Changed rules in `tao/pegtl/contrib/integer.hpp` to not accept redundant leading zeros.
-* Added rules to `tao/pegtl/contrib/integer.hpp` that test unsigned values against a maximum.
-* Removed option of [state](Rule-Reference.md#state-s-r-)'s `S::success()` to have an extended signature to get access to the current `apply_mode`, `rewind_mode`, *action*- and *control* class (template).
-* Added `[[nodiscard]]` or `[[noreturn]]` to most non-void functions.
-* Removed compatibility macros starting with `TAOCPP_PEGTL_`.
-* Removed compatibility uppercase enumerators.
-* Removed compatibility `peek_byte()` member functions.
-* Removed compatibility header `changes.hpp` from contrib.
-* Demoted UTF-16 and UTF-32 support to contrib.
-* Demoted UINT-8, UINT-16, UINT-32 and UINT-64 support to contrib.
-* Folded `contrib/counter.hpp` into `json_count.cpp`, count is superceded by coverage.
-* Refactored demangling.
-  * Improves generated code to be shorter and more efficient.
-  * Removes the need for RTTI.
-  * Some broken/unknown compilers will use RTTI as a fallback, without demangling.
-* Refactored parse tree type storage/handling.
-  * Removes the need for RTTI.
+* Infrastructure
+  * Updated required C++ standard to C++17.
+  * Updated required [CMake](https://cmake.org/) version to 3.8.
+  * The macro `TAO_PEGTL_NAMESPACE` now contains the fully qualified namespace, e.g. `tao::pegtl`.
+  * Added `[[nodiscard]]` or `[[noreturn]]` to most non-void functions.
+* Meta-Data Layer
+  * Replaced `analysis_t` with more general and complete `rule_t` and `subs_t`.
+  * Added functions to visit all rules of a grammar.
+  * Added functions to measure rule coverage of a parsing run.
+  * Moved the analysis function and header to contrib.
+* Error Handling
+  * Replaced `tao::pegtl::input_error` with `std::system_error` and `std::filesystem::filesystem_error`.
+  * Added [`must_if<>`](Errors-and-Exceptions.md#custom-exception-messages)
+    * Allows to define custom error messages for global errors.
+    * Adds a non-intrusive way to define global parse errors for a grammar retroactively.
+* Demangling
+  * Removed the need for RTTI.
+    * Some broken/unknown compilers will use RTTI as a fallback, without demangling.
+  * Moved `tao::pegtl::internal::demangle<T>()` to `tao::demangle<T>()`.
+  * Improved generated code to be shorter and more efficient.
+* Parse Tree
+  * Removed the need for RTTI.
+* Other
+  * Changed `std::string` to `std::filesystem::path` for filename parameters.
+  * Renamed `byte_in_line` to `column` and use 1-based counting.
+  * Moved rule `eolf` from inline namespace `tao::pegtl::ascii` to `tao::pegtl`.
+  * Changed rules in `tao/pegtl/contrib/integer.hpp` to not accept redundant leading zeros.
+  * Added rules to `tao/pegtl/contrib/integer.hpp` that test unsigned values against a maximum.
+  * Demoted UTF-16 and UTF-32 support to contrib.
+  * Demoted UINT-8, UINT-16, UINT-32 and UINT-64 support to contrib.
+  * Folded `contrib/counter.hpp` into `json_count.cpp`, count is superceded by coverage.
+* Cleanup
+  * Removed option of [state](Rule-Reference.md#state-s-r-)'s `S::success()` to have an extended signature to get access to the current `apply_mode`, `rewind_mode`, *action*- and *control* class (template).
+  * Removed compatibility macros starting with `TAOCPP_PEGTL_`.
+  * Removed compatibility uppercase enumerators.
+  * Removed compatibility `peek_byte()` member functions.
+  * Removed compatibility header `changes.hpp` from contrib.
+
+## 2.8.3
+
+Released 2020-04-22
+
+* Fixed excessive read-ahead with incremental inputs.
+* Added state manipulators `remove_first_state`, `remove_last_states`, `rotate_states_right`, `rotate_states_left`, and `reverse_states` to contrib.
+* Reduced the number of intermediate parse tree nodes.
 
 ## 2.8.2
 
diff --git a/packages/PEGTL/doc/Control-and-Debug.md b/packages/PEGTL/doc/Control-and-Debug.md
index c8817e611e8a84e2c7894da571b74798bf6edf7c..7d801ef358f8822937251303bf3991abdac4b9ee 100644
--- a/packages/PEGTL/doc/Control-and-Debug.md
+++ b/packages/PEGTL/doc/Control-and-Debug.md
@@ -71,6 +71,13 @@ struct normal
 
 The static member functions `start()`, `success()` and `failure()` can be used to debug a grammar by using them to provide insight into what exactly is going on during a parsing run, or to construct a parse tree, etc.
 
+There is one more, *optional* hook function: `unwind()`.
+It is called when a rule throws an exception, e.g. on global error.
+It's signature is identical to `start()`/`success()`/`failure()`.
+It is not included in the default control template `normal`, as the existence of an `unwind()` method requires an additional `try`/`catch` block.
+This might potentially have an impact on the binary size and therefore, one should only add an `unwind()` method if necessary.
+Several other control classes utilize the `unwind()` method to track the execution even in the presence of global errors.
+
 The static member function `raise()` is used to create a global error, and any replacement should again throw an exception, or abort the application.
 
 The static member functions `apply()` and `apply0()` can customise how actions with, and without, receiving the matched input are called, respectively.
@@ -89,17 +96,13 @@ Before attempting to match a rule `R`, the PEGTL calls `C< R >::start()` where `
 Depending on what happens during the attempt to match `R`, one of the other three functions might be called.
 
 - If `R` succeeds, then `C< R >::success()` is called; compared to the call to `C< R >::start()`, the input will have consumed whatever the successful match of `R` consumed.
-
-- If `R` finishes with a failure, i.e. a return value of `false` from `match()`, then `C< R >::failure()` is called; a failed match **must not** consume input.
-
+- If `R` finishes with a local failure, i.e. a return value of `false` from `match()`, then `C< R >::failure()` is called; a failed match **must not** consume input.
+- If `R` finishes with a global failure, i.e. an exception being thrown from `match()`, then, if present, `C< R >::unwind()` is called.
 - If `R` is wrapped in `must< R >`, a global failure is generated by calling `C< R >::raise()` to throw some exception as is expected by the PEGTL in this case.
 
-- If a sub-rule of `R` finishes with a global failure, and the exception is not caught by a `try_catch` or similar combinator, then no other function of `C< R >` is called after `C< R >::start()`.
-
 Additionally, if matching `R` was successful and actions are enabled:
 
 - If `C< R >::apply()` exists, then `C< R >::apply()` is called with the matched input and the current state arguments.
-
 - If `C< R >::apply0()` exists, then `C< R >::apply0()` is called with the current state arguments.
 
 It is an error when both `C< R >::apply()` and `C< R >::apply0()` exist.
@@ -112,7 +115,6 @@ If either produce a (local) failure then `C< R >::failure()` is called.
 In all cases where an action is called, the success or failure hooks are invoked after the action returns.
 
 The included class `tao::pegtl::tracer` in `<tao/pegtl/contrib/tracer.hpp>` gives a practical example that can be used as control class to debug grammars.
-When an instance of class `tao::pegtl::trace_state` is used as single state in a parsing run with the tracer-control then the debug output contains a line number and rule number as additional information.
 
 ## Exception Throwing
 
diff --git a/packages/PEGTL/doc/Errors-and-Exceptions.md b/packages/PEGTL/doc/Errors-and-Exceptions.md
index 7cd752ef7eb009d7489070f9286d447613113e58..10408129978e2abd86bfced826725a9e9a519714 100644
--- a/packages/PEGTL/doc/Errors-and-Exceptions.md
+++ b/packages/PEGTL/doc/Errors-and-Exceptions.md
@@ -6,11 +6,12 @@ A parsing run, a call to one of the `parse()` functions as explained in [Inputs
 * A return value of `false` is called a *local failure* (even when propagated to the top).
 * An exception indicating a *global failure* is thrown.
 
-The PEGTL parsing rules throw exceptions of type `tao::pegtl::parse_error`, some of the inputs throw exceptions of type `tao::pegtl::input_error`.
+The PEGTL parsing rules throw exceptions of type `tao::pegtl::parse_error`, some of the inputs throw additional exceptions like `std::system_error` or `std::filesystem_error`.
 Other exception classes can be used freely from actions and custom parsing rules.
 
 ## Contents
 
+* [Global Failure](#global-failure)
 * [Local to Global Failure](#local-to-global-failure)
   * [Intrusive Local to Global Failure](#intrusive-local-to-global-failure)
   * [Non-Intrusive Local to Global Failure](#non-intrusive-local-to-global-failure)
@@ -18,6 +19,52 @@ Other exception classes can be used freely from actions and custom parsing rules
 * [Examples for Must Rules](#examples-for-must-rules)
 * [Custom Exception Messages](#custom-exception-messages)
 
+## Global Failure
+
+By default, global failure means that an exception of type `tao::pegtl::parse_error` is thrown.
+
+Synposis:
+
+```c++
+namespace tao::pegtl
+{
+   class parse_error
+      : public std::runtime_error
+   {
+      parse_error( const char* msg, position p );
+
+      parse_error( const std::string& msg, position p )
+         : parse_error( msg.c_str(), std::move( p ) )
+      {}
+
+      template< typename ParseInput >
+      parse_error( const char* msg, const ParseInput& in )
+         : parse_error( msg, in.position() )
+      {}
+
+      template< typename ParseInput >
+      parse_error( const std::string& msg, const ParseInput& in )
+         : parse_error( msg, in.position() )
+      {}
+
+      const char* what() const noexcept override;
+
+      std::string_view message() const noexcept;
+      const std::vector< position >& positions() const noexcept;
+
+      void add_position( position&& p );
+   };
+}
+```
+
+The `what()` message will contain all positions as well as the original `msg`.
+This allows retrieval of all information if the exception is handled as a `std::runtime_error` in a generic way.
+
+The `message()` function will return the original `msg`, while `positions()` allows access to the stored positions.
+This is useful to decompose the exception and provide more helpful errors to the user.
+
+The constructors can be used by custom rules to signal global failure, while `add_position()` is often used when you are parsing nested data, so you can append the position in the original file which includes the nested file.
+
 ## Local to Global Failure
 
 ### Intrusive Local to Global Failure
@@ -144,14 +191,25 @@ struct error { template< typename Rule > static constexpr auto message = error_m
 template< typename Rule > using control = tao::pegtl::must_if< error >::control< Rule >;
 ```
 
+`must_if<>` expects a wrapper for the error message as its first template parameter.
+There is a second parameter for the base control class, which defaults to `tao::pegtl::normal`, and which can combine `must_if`'s control class with other control classes.
+
 Since `raise()` is only instantiated for those rules for which `must<>` could trigger an exception, it is sufficient to provide specialisations of the error message string for those rules.
 Furthermore, there will be a compile-time error (i.e. a `static_assert`) for all rules for which the specialisation was forgotten although `raise()` could be called.
 
-The [control class](Control-and-Debug.md) provided by `must_if<>` also turns local failures into global failure if an error message is provided, i.e. if the error message is not `nullptr`.
+The [control class](Control-and-Debug.md) provided by `must_if<>` also turns, by default, local failures into global failure if an error message is provided, i.e. if the error message is not `nullptr`.
 This means that one can provide additional points in the grammar where a global failure is triggered, even when the grammar contains no `must<>` error points.
 
-`must_if<>` expects a wrapper for the error message as its first template parameter.
-There is a second parameter for the base control class, which defaults to `tao::pegtl::normal`, and which can combine `must_if`'s control class with other control classes.
+The above feature also means that a rule which is used both with and without `must<>`, one would not only provide a custom error message for the location where the rule is failing within a `must<>`-context, but local errors in other contexts are implicitly turned into global error.
+If this behaviour is not intended, one can disable the "turn local to global failure" feature by setting `raise_on_failure` to `false` in the wrapper class:
+
+```c++
+struct error
+{
+   template< typename Rule > static constexpr bool raise_on_failure = false;
+   template< typename Rule > static constexpr auto message = error_message< Rule >;
+};
+```
 
 It is advisable to choose the error points in the grammar with prudence.
 This choice becoming particularly cumbersome and/or resulting in a large number of error points might be an indication of the grammar needing some kind of simplification or restructuring.
diff --git a/packages/PEGTL/doc/Getting-Started.md b/packages/PEGTL/doc/Getting-Started.md
index d2ef73f544ed8e824f94101b2c967c789e9227bc..50674bd2af263fd471050316fc5f84dbc31f25d1 100644
--- a/packages/PEGTL/doc/Getting-Started.md
+++ b/packages/PEGTL/doc/Getting-Started.md
@@ -4,9 +4,6 @@ Since the PEGTL is a parser library, here is an "inverse hello world" example th
 rather than prints, the string `Hello, foo!` for any sequence of alphabetic ASCII characters `foo`.
 
 ```c++
-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
 #include <string>
 #include <iostream>
 
@@ -98,7 +95,174 @@ terminate called after throwing an instance of 'tao::pegtl::parse_error'
 Aborted (core dumped)
 ```
 
-Frequently an application will include a top-level `try-catch` block to handle
-the exception.
+The PEGTL provides multiple facilities that help to get started and develop your grammar.
+In the following paragraphs we will show several small programs to showcase the capabilities of the PEGTL.
+
+Note, however, that all examples shown in this document will lack proper error handling.
+Frequently an application will include `try-catch` blocks to handle the exceptions.
+The correct way of handling errors is shown at the last paragraph of this page.
+
+## Grammar Analysis
+
+Every grammar must be free of cycles that make no progress, i.e. the cycle does not consume any input.
+This is a common problem in parsing called [left recursion](https://en.wikipedia.org/wiki/Left_recursion).
+Especially with the PEG formalism, it results in an infinite loop and, eventually, in a stack overflow.
+
+The PEGTL provides a [grammar analysis](Grammar-Analysis.md) with which a grammar can be verified.
+Note that this is done at runtime as a pure compile-time analysis would lead to insupportable compile-times.
+The analysis, however, is only based on the grammar itself and not on a specific input.
+Additionally, the analysis is typically written as a separate program to keep any overhead from your normal applications.
+
+```c++
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/analyze.hpp>
+
+// This example uses the included JSON grammar
+#include <tao/pegtl/contrib/json.hpp>
+
+namespace pegtl = tao::pegtl;
+
+using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+int main()
+{
+   if( pegtl::analyze< grammar >() != 0 ) {
+      std::cerr << "cycles without progress detected!\n";
+      return 1;
+   }
+
+   return 0;
+}
+```
+
+## Tracer
+
+One of the most basic tools when developing a grammar is a tracer that prints every step of a parsing run.
+The PEGTL provides a tracer that will print to stderr, as well as allowing users to write their own tracers to output other formats.
+
+```c++
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/trace.hpp>
+
+// This example uses the included JSON grammar
+#include <tao/pegtl/contrib/json.hpp>
+
+namespace pegtl = tao::pegtl;
+
+using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+int main( int argc, char** argv )
+{
+   if( argc != 2 ) return 1;
+
+   pegtl::argv_input in( argv, i );
+   pegtl::standard_tracer tr( in );
+   tr.parse< grammar >( in );
+
+   return 0;
+}
+```
+
+In the above each command line parameter is parsed as a JSON string.
+As the output gets long quickly, we will not show it here, please have a look at the [Tracer](Tracer.md) documentation.
+
+TODO: Write `Tracer.md`.
+
+## Parse Tree / AST
+
+When developing grammars, a common goal is to generate a [parse tree](https://en.wikipedia.org/wiki/Parse_tree) or an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree).
+
+The PEGTL provides a [Parse Tree](Parse-Tree.md) builder that can filter and/or transform tree nodes on-the-fly.
+Additionally, a helper is provided to print out the resulting data structure in the [DOT](https://en.wikipedia.org/wiki/DOT_(graph_description_language)) format, suitable for creating a graphical representation of the parse tree.
+
+The following example uses a selector to filter the parse tree nodes, as otherwise the graphical representation may become confusing quite quickly.
+
+```c++
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/parse_tree.hpp>
+#include <tao/pegtl/contrib/parse_tree_to_dot.hpp>
+
+// This example uses the included JSON grammar
+#include <tao/pegtl/contrib/json.hpp>
+
+namespace pegtl = tao::pegtl;
+
+using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+template< typename Rule >
+using selector = pegtl::parse_tree::selector<
+   Rule,
+   pegtl::parse_tree::store_content::on<
+      pegtl::json::null,
+      pegtl::json::true_,
+      pegtl::json::false_,
+      pegtl::json::number,
+      pegtl::json::string,
+      pegtl::json::key,
+      pegtl::json::array,
+      pegtl::json::object,
+      pegtl::json::member > >;
+
+int main( int argc, char** argv )
+{
+   if( argc != 2 ) return 1;
+
+   pegtl::argv_input in( argv, i );
+   const auto root = parse_tree::parse< grammar, selector >( in );
+   if( root ) {
+      parse_tree::print_dot( std::cout, *root );
+   }
+
+   return 0;
+}
+```
+
+Running the above program with some example input:
+
+```sh
+$ build/src/example/pegtl/json_parse_tree '{"foo":[true,{}],"bar":[42,null]}' | dot -Tsvg -o json_parse_tree.svg
+```
+
+The above will generate an SVG file with a graphical representation of the parse tree.
+
+![JSON Parse Tree](Json-Parse-Tree.svg)
+
+## Error Handling
+
+Although the PEGTL could be used without exceptions, most programs will use input classes or grammars that might throw exceptions.
+Typically, the following pattern helps to print the exceptions properly:
+
+```c++
+   // The surrounding try/catch for normal exceptions.
+   // These might occur if a file can not be opened, etc.
+   try {
+      tao::pegtl::file_input in( filename );
+
+      // The inner try/catch block, see below...
+      try {
+
+         // The actual parser, tracer, parse tree, ...
+         pegtl::parse< grammar >( in );
+
+      }
+      catch( const pegtl::parse_error& e ) {
+
+         // This catch block needs access to the input
+         const auto p = e.positions().front();
+         std::cerr << e.what() << '\n'
+                   << in.line_at( p ) << '\n'
+                   << std::setw( p.column ) << '^' << std::endl;
+
+      }
+   }
+   catch( const std::exception& e ) {
+
+      // Generic catch block for other exceptions
+      std::cerr << e.what() << std::endl;
+
+   }
+```
+
+For more information see [Errors and Exceptions](Errors-and-Exceptions.md).
 
 Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
diff --git a/packages/PEGTL/doc/Inputs-and-Parsing.md b/packages/PEGTL/doc/Inputs-and-Parsing.md
index 689f0938c74c7c99f6dec5555dd554839524b925..2f00c8661cb61e5cdbbbb96f1446d2b7d9899d7d 100644
--- a/packages/PEGTL/doc/Inputs-and-Parsing.md
+++ b/packages/PEGTL/doc/Inputs-and-Parsing.md
@@ -51,7 +51,7 @@ All classes and functions on this page are in namespace `tao::pegtl`.
 
 ## Tracking Mode
 
-Some input classes allow a choice of tracking mode, or whether the `byte`, `line` and `byte_in_line` counters are continuously updated during a parsing run with `tracking_mode::eager`, or only calculated on-demand in `position()` by scanning the complete input again with `tracking_mode::lazy`.
+Some input classes allow a choice of tracking mode, or whether the `byte`, `line` and `column` counters are continuously updated during a parsing run with `tracking_mode::eager`, or only calculated on-demand in `position()` by scanning the complete input again with `tracking_mode::lazy`.
 
 Lazy tracking is recommended when the position is used very infrequently, for example only in the case of throwing a `parse_error`.
 
@@ -78,7 +78,6 @@ The classes `file_input<>`, `read_input<>` and, on supported platforms, `mmap_in
 * `mmap_input<>` uses `mmap(2)` on POSIX compliant systems or `MapViewOfFile()` on Windows.
 * `file_input<>` is derived from `mmap_input<>` when available, and `read_input<>` otherwise, inheriting the respective constructors.
 
-Most file input classes take a single argument, the filename, which can be supplied as `std::string` or `const char*`.
 They immediately make available the complete contents of the file; `read_input<>` reads the entire file upon construction.
 
 The constructors that take a `FILE*` argument take ownership of the file pointer, i.e. they `fclose()` it in the destructor.
@@ -87,27 +86,24 @@ The constructors that take a `FILE*` argument take ownership of the file pointer
 template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf >
 struct read_input
 {
-   explicit read_input( const char* filename );
-   explicit read_input( const std::string& filename );
+   explicit read_input( const std::filesystem::path& path );
+   read_input( const std::filesystem::path& path, const std::string& source );
 
-   read_input( FILE* file, const char* filename );
-   read_input( FILE* file, const std::string& filename );
+   read_input( FILE* file, const std::filesystem::path& path );
+   read_input( FILE* file, const std::filesystem::path& path, const std::string& source );
 };
 
 template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf >
 struct mmap_input
 {
-   explicit mmap_input( const char* filename );
-   explicit mmap_input( const std::string& filename );
+   explicit mmap_input( const std::filesystem::path& path );
+   mmap_input( const std::filesystem::path& path, const std::string& source );
 };
 
 template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf >
 using file_input = mmap_input< P, Eol >;  // Or read_input when no mmap_input available.
 ```
 
-Note that the implementation of the constructors is different than shown.
-They should be used "as if" this was the actual signature.
-
 ## Memory Input
 
 The class `memory_input<>` can be used to parse existing contiguous blocks of memory like the contents of a `std::string`.
@@ -119,7 +115,7 @@ If you don't want to specify a source just use the empty string (`""`).
 The constructors that only takes a `const char* begin` for the data uses `std::strlen()` to determine the length.
 It will therefore *only* work correctly with data that is terminated with a 0-byte (and does not contain embedded 0-bytes, which are otherwise fine).
 
-The constructors that take additional `byte`, `line` and `byte_in_line` arguments initialise the internal counters with the supplied values, rather than the defaults of `0`, `1` and `0`.
+The constructors that take additional `byte`, `line` and `column` arguments initialise the internal counters with the supplied values, rather than the defaults of `0`, `1` and `1`.
 
 ```c++
 template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf, typename Source = std::string >
@@ -142,7 +138,7 @@ class memory_input
 
    template< typename T >
    memory_input( const char* begin, const char* end, T&& source,
-                 const std::size_t byte, const std::size_t line, const std::size_t byte_in_line ) noexcept(...);
+                 const std::size_t byte, const std::size_t line, const std::size_t column ) noexcept(...);
 };
 ```
 
@@ -207,7 +203,7 @@ class string_input
 
    template< typename V, typename T >
    string_input( V&& data, T&& source,
-                 const std::size_t byte, const std::size_t line, const std::size_t byte_in_line ) noexcept(...);
+                 const std::size_t byte, const std::size_t line, const std::size_t column ) noexcept(...);
 };
 ```
 
@@ -532,10 +528,10 @@ try {
   // call parse on the input 'in' here...
 }
 catch( const parse_error& e ) {
-   const auto p = e.positions.front();
+   const auto p = e.positions().front();
    std::cerr << e.what() << std::endl
-             << in.line_at( p ) << std::endl
-             << std::string( p.byte_in_line, ' ' ) << '^' << std::endl;
+             << in.line_at( p ) << '\n'
+             << std::setw( p.column ) << '^' << std::endl;
 }
 ```
 
diff --git a/packages/PEGTL/doc/Installing-and-Using.md b/packages/PEGTL/doc/Installing-and-Using.md
index ec4302e1314688def828e3c9f9737b5f0202c3bc..91fc2efec9a772708ab72d1c8aaa3b27193016a1 100644
--- a/packages/PEGTL/doc/Installing-and-Using.md
+++ b/packages/PEGTL/doc/Installing-and-Using.md
@@ -21,10 +21,15 @@
 
 The PEGTL requires a C++17-capable compiler, e.g. one of
 
-* GCC 7
+* GCC 8
 * Clang 5
 * Visual Studio 2017
 
+with (on Unix)
+
+* libstdc++ 8
+* libc++ 7
+
 on either
 
 * Linux
@@ -34,6 +39,8 @@ on either
 It requires C++17, e.g. using the `--std=c++17` compiler switch.
 Using newer versions of the C++ standard is supported.
 
+Note that using libstdc++ 8 requires linking with `-lstdc++fs`.
+
 Larger projects will frequently require the `/bigobj` option when compiling with Visual Studio on Windows.
 
 It should also work with other C++17 compilers on other Unix systems (or any sufficiently compatible platform).
@@ -50,6 +57,8 @@ Note that some of the listed packages are not updated regularly.
 
 ## Using Conan
 
+[![Download](https://api.bintray.com/packages/conan/conan-center/taocpp-pegtl%3A_/images/download.svg)](https://bintray.com/conan/conan-center/taocpp-pegtl%3A_/_latestVersion)
+
 You can download and install the PEGTL using the [Conan] package manager:
 
 ```bash
@@ -246,9 +255,8 @@ $ make amalgamate
 ```
 
 The above will generate a `build/amalgamated/pegtl.hpp` which will consist of
-the headers `tao/pegtl.hpp`, `tao/pegtl/analyze.hpp`, their dependencies,
-and all headers in `tao/pegtl/contrib/` except for the headers in
-`tao/pegtl/contrib/icu/`.
+the headers `tao/pegtl.hpp`, their dependencies, and all headers in
+`tao/pegtl/contrib/` except for the headers in `tao/pegtl/contrib/icu/`.
 
 Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
 
diff --git a/packages/PEGTL/doc/Json-Parse-Tree.svg b/packages/PEGTL/doc/Json-Parse-Tree.svg
new file mode 100644
index 0000000000000000000000000000000000000000..27d62db25f61291e3298077f1f0f2d3b9929ade2
--- /dev/null
+++ b/packages/PEGTL/doc/Json-Parse-Tree.svg
@@ -0,0 +1,162 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
+ "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<!-- Generated by graphviz version 2.40.1 (0)
+ -->
+<!-- Title: parse_tree Pages: 1 -->
+<svg width="1205pt" height="403pt"
+ viewBox="0.00 0.00 1205.45 402.96" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<g id="graph0" class="graph" transform="scale(1 1) rotate(0) translate(4 398.9605)">
+<title>parse_tree</title>
+<polygon fill="#ffffff" stroke="transparent" points="-4,4 -4,-398.9605 1201.4457,-398.9605 1201.4457,4 -4,4"/>
+<!-- x0x1690eb0 -->
+<g id="node1" class="node">
+<title>x0x1690eb0</title>
+<ellipse fill="none" stroke="#000000" cx="485.0158" cy="-376.9605" rx="38.1938" ry="18"/>
+<text text-anchor="middle" x="485.0158" y="-373.2605" font-family="Times,serif" font-size="14.00" fill="#000000">ROOT</text>
+</g>
+<!-- x0x1691240 -->
+<g id="node2" class="node">
+<title>x0x1691240</title>
+<ellipse fill="none" stroke="#000000" cx="485.0158" cy="-296.0904" rx="194.4087" ry="26.7407"/>
+<text text-anchor="middle" x="485.0158" y="-299.8904" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::object</text>
+<text text-anchor="middle" x="485.0158" y="-284.8904" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;{\&quot;foo\&quot;:[true,{}],\&quot;bar\&quot;:[42,null]}&quot;</text>
+</g>
+<!-- x0x1690eb0&#45;&gt;x0x1691240 -->
+<g id="edge1" class="edge">
+<title>x0x1690eb0&#45;&gt;x0x1691240</title>
+<path fill="none" stroke="#000000" d="M485.0158,-358.6098C485.0158,-351.0399 485.0158,-341.9805 485.0158,-333.1385"/>
+<polygon fill="#000000" stroke="#000000" points="488.5159,-332.961 485.0158,-322.961 481.5159,-332.9611 488.5159,-332.961"/>
+</g>
+<!-- x0x16913f0 -->
+<g id="node3" class="node">
+<title>x0x16913f0</title>
+<ellipse fill="none" stroke="#000000" cx="341.0158" cy="-206.3503" rx="135.1148" ry="26.7407"/>
+<text text-anchor="middle" x="341.0158" y="-210.1503" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::member</text>
+<text text-anchor="middle" x="341.0158" y="-195.1503" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;\&quot;foo\&quot;:[true,{}]&quot;</text>
+</g>
+<!-- x0x1691240&#45;&gt;x0x16913f0 -->
+<g id="edge2" class="edge">
+<title>x0x1691240&#45;&gt;x0x16913f0</title>
+<path fill="none" stroke="#000000" d="M442.5899,-269.6508C426.341,-259.5246 407.6599,-247.8826 390.7852,-237.3663"/>
+<polygon fill="#000000" stroke="#000000" points="392.4859,-234.3022 382.1479,-231.9836 388.7836,-240.243 392.4859,-234.3022"/>
+</g>
+<!-- x0x1691870 -->
+<g id="node4" class="node">
+<title>x0x1691870</title>
+<ellipse fill="none" stroke="#000000" cx="629.0158" cy="-206.3503" rx="135.1148" ry="26.7407"/>
+<text text-anchor="middle" x="629.0158" y="-210.1503" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::member</text>
+<text text-anchor="middle" x="629.0158" y="-195.1503" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;\&quot;bar\&quot;:[42,null]&quot;</text>
+</g>
+<!-- x0x1691240&#45;&gt;x0x1691870 -->
+<g id="edge3" class="edge">
+<title>x0x1691240&#45;&gt;x0x1691870</title>
+<path fill="none" stroke="#000000" d="M527.4417,-269.6508C543.6905,-259.5246 562.3716,-247.8826 579.2464,-237.3663"/>
+<polygon fill="#000000" stroke="#000000" points="581.2479,-240.243 587.8837,-231.9836 577.5456,-234.3022 581.2479,-240.243"/>
+</g>
+<!-- x0x1691510 -->
+<g id="node5" class="node">
+<title>x0x1691510</title>
+<ellipse fill="none" stroke="#000000" cx="111.0158" cy="-116.6102" rx="111.0315" ry="26.7407"/>
+<text text-anchor="middle" x="111.0158" y="-120.4102" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::key</text>
+<text text-anchor="middle" x="111.0158" y="-105.4102" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;\&quot;foo\&quot;&quot;</text>
+</g>
+<!-- x0x16913f0&#45;&gt;x0x1691510 -->
+<g id="edge4" class="edge">
+<title>x0x16913f0&#45;&gt;x0x1691510</title>
+<path fill="none" stroke="#000000" d="M279.38,-182.3016C248.5091,-170.2566 211.0461,-155.6394 179.3035,-143.2543"/>
+<polygon fill="#000000" stroke="#000000" points="180.2378,-139.8619 169.6496,-139.4876 177.6934,-146.3831 180.2378,-139.8619"/>
+</g>
+<!-- x0x16916c0 -->
+<g id="node6" class="node">
+<title>x0x16916c0</title>
+<ellipse fill="none" stroke="#000000" cx="360.0158" cy="-116.6102" rx="120.4167" ry="26.7407"/>
+<text text-anchor="middle" x="360.0158" y="-120.4102" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::array</text>
+<text text-anchor="middle" x="360.0158" y="-105.4102" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;[true,{}]&quot;</text>
+</g>
+<!-- x0x16913f0&#45;&gt;x0x16916c0 -->
+<g id="edge5" class="edge">
+<title>x0x16913f0&#45;&gt;x0x16916c0</title>
+<path fill="none" stroke="#000000" d="M346.7164,-179.4253C348.4448,-171.2616 350.3742,-162.1491 352.2154,-153.4525"/>
+<polygon fill="#000000" stroke="#000000" points="355.6719,-154.0243 354.3192,-143.5162 348.8237,-152.5743 355.6719,-154.0243"/>
+</g>
+<!-- x0x1691900 -->
+<g id="node9" class="node">
+<title>x0x1691900</title>
+<ellipse fill="none" stroke="#000000" cx="619.0158" cy="-116.6102" rx="111.0315" ry="26.7407"/>
+<text text-anchor="middle" x="619.0158" y="-120.4102" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::key</text>
+<text text-anchor="middle" x="619.0158" y="-105.4102" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;\&quot;bar\&quot;&quot;</text>
+</g>
+<!-- x0x1691870&#45;&gt;x0x1691900 -->
+<g id="edge8" class="edge">
+<title>x0x1691870&#45;&gt;x0x1691900</title>
+<path fill="none" stroke="#000000" d="M626.0154,-179.4253C625.1156,-171.3504 624.1124,-162.3471 623.1528,-153.7362"/>
+<polygon fill="#000000" stroke="#000000" points="626.6,-153.067 622.014,-143.5162 619.6431,-153.8423 626.6,-153.067"/>
+</g>
+<!-- x0x1691c70 -->
+<g id="node10" class="node">
+<title>x0x1691c70</title>
+<ellipse fill="none" stroke="#000000" cx="868.0158" cy="-116.6102" rx="120.4167" ry="26.7407"/>
+<text text-anchor="middle" x="868.0158" y="-120.4102" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::array</text>
+<text text-anchor="middle" x="868.0158" y="-105.4102" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;[42,null]&quot;</text>
+</g>
+<!-- x0x1691870&#45;&gt;x0x1691c70 -->
+<g id="edge9" class="edge">
+<title>x0x1691870&#45;&gt;x0x1691c70</title>
+<path fill="none" stroke="#000000" d="M692.4359,-182.5372C724.5318,-170.4858 763.6079,-155.8134 796.7409,-143.3726"/>
+<polygon fill="#000000" stroke="#000000" points="798.2351,-146.5502 806.3666,-139.7583 795.7745,-139.9969 798.2351,-146.5502"/>
+</g>
+<!-- x0x1691480 -->
+<g id="node7" class="node">
+<title>x0x1691480</title>
+<ellipse fill="none" stroke="#000000" cx="154.0158" cy="-26.8701" rx="119.5021" ry="26.7407"/>
+<text text-anchor="middle" x="154.0158" y="-30.6701" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::true_</text>
+<text text-anchor="middle" x="154.0158" y="-15.6701" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;true&quot;</text>
+</g>
+<!-- x0x16916c0&#45;&gt;x0x1691480 -->
+<g id="edge6" class="edge">
+<title>x0x16916c0&#45;&gt;x0x1691480</title>
+<path fill="none" stroke="#000000" d="M305.0822,-92.6794C278.4184,-81.0638 246.2148,-67.0349 218.403,-54.9192"/>
+<polygon fill="#000000" stroke="#000000" points="219.7196,-51.6751 209.1539,-50.89 216.9239,-58.0926 219.7196,-51.6751"/>
+</g>
+<!-- x0x1691b50 -->
+<g id="node8" class="node">
+<title>x0x1691b50</title>
+<ellipse fill="none" stroke="#000000" cx="416.0158" cy="-26.8701" rx="124.4016" ry="26.7407"/>
+<text text-anchor="middle" x="416.0158" y="-30.6701" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::object</text>
+<text text-anchor="middle" x="416.0158" y="-15.6701" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;{}&quot;</text>
+</g>
+<!-- x0x16916c0&#45;&gt;x0x1691b50 -->
+<g id="edge7" class="edge">
+<title>x0x16916c0&#45;&gt;x0x1691b50</title>
+<path fill="none" stroke="#000000" d="M376.8176,-89.6852C382.1888,-81.0778 388.2182,-71.4157 393.9077,-62.2983"/>
+<polygon fill="#000000" stroke="#000000" points="396.901,-64.1127 399.2258,-53.776 390.9624,-60.4068 396.901,-64.1127"/>
+</g>
+<!-- x0x1691630 -->
+<g id="node11" class="node">
+<title>x0x1691630</title>
+<ellipse fill="none" stroke="#000000" cx="822.0158" cy="-26.8701" rx="132.8722" ry="26.7407"/>
+<text text-anchor="middle" x="822.0158" y="-30.6701" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::number</text>
+<text text-anchor="middle" x="822.0158" y="-15.6701" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;42&quot;</text>
+</g>
+<!-- x0x1691c70&#45;&gt;x0x1691630 -->
+<g id="edge10" class="edge">
+<title>x0x1691c70&#45;&gt;x0x1691630</title>
+<path fill="none" stroke="#000000" d="M854.2142,-89.6852C849.8932,-81.2553 845.0535,-71.8137 840.4654,-62.863"/>
+<polygon fill="#000000" stroke="#000000" points="843.4838,-61.0785 835.8075,-53.776 837.2545,-64.2716 843.4838,-61.0785"/>
+</g>
+<!-- x0x1691e80 -->
+<g id="node12" class="node">
+<title>x0x1691e80</title>
+<ellipse fill="none" stroke="#000000" cx="1085.0158" cy="-26.8701" rx="112.36" ry="26.7407"/>
+<text text-anchor="middle" x="1085.0158" y="-30.6701" font-family="Times,serif" font-size="14.00" fill="#000000">tao::pegtl::json::null</text>
+<text text-anchor="middle" x="1085.0158" y="-15.6701" font-family="Times,serif" font-size="14.00" fill="#000000">&quot;null&quot;</text>
+</g>
+<!-- x0x1691c70&#45;&gt;x0x1691e80 -->
+<g id="edge11" class="edge">
+<title>x0x1691c70&#45;&gt;x0x1691e80</title>
+<path fill="none" stroke="#000000" d="M925.3138,-92.9146C954.0934,-81.0129 989.1091,-66.5322 1019.0107,-54.1664"/>
+<polygon fill="#000000" stroke="#000000" points="1020.6216,-57.2878 1028.525,-50.2318 1017.9464,-50.8191 1020.6216,-57.2878"/>
+</g>
+</g>
+</svg>
diff --git a/packages/PEGTL/doc/Meta-Data-and-Visit.md b/packages/PEGTL/doc/Meta-Data-and-Visit.md
index e8c5f5223abe862508dab87112698284c40851a4..3f8207553087b91920fc06f03f7fe54b15da718e 100644
--- a/packages/PEGTL/doc/Meta-Data-and-Visit.md
+++ b/packages/PEGTL/doc/Meta-Data-and-Visit.md
@@ -83,10 +83,68 @@ Unlike `visit()`, `visit_rt()` returns the number of rules visited.
 
 ## Grammar Print
 
-TODO.
+The functions `print_rules()` and `print_sub_rules()` from `include/tao/pegtl/contrib/print.hpp` combine the `visit()` function with visitors that print some information about all (sub-)rules of the supplied grammar to the supplied `std::ostream`.
+
+See `src/example/pegtl/json_print_rules.cpp` and `src/example/pegtl/json_print_sub_rules.cpp` for how to use these functions, and what the output looks like.
+As expected, the `internal` sub-rules are printed, too.
 
 ## Rule Coverage
 
-TODO.
+The function `coverage()` from `include/tao/pegtl/contrib/coverage.hpp` is very similar to the `parse()` function.
+It is called like `parse()`, with the some of the same template parameters and all of the same function arguments, however it returns an object of type `coverage_result` instead of a `bool`.
+
+```c++
+template< typename Rule,
+          template< typename... > class Action = nothing,
+          template< typename... > class Control = normal,
+          typename ParseInput,
+          typename... States >
+coverage_result coverage( ParseInput& in,
+                          States&&... st );
+```
+
+After a parsing run, the `coverage_result` indicates whether the run was a success or a failure, and contains "rule coverage" and "branch coverage" information.
+
+The "rule coverage" shows how often each rule was attempted to match, and how many of these attempts were a success or a -- local or global -- failure.
+
+The "branch coverage" consists in the matching information also being recorded for each immediate sub-rule of every rule; in the case of an `sor<>` this shows how often each sub-rule was taken, hence the name.
+
+The coverage information in the `coverage_result` can either be inspected and processed or printed manually, or the `ostream` output `operator<<` from `include/tao/pegtl/contrib/print_coverage.hpp` can be used.
+The operator formats the output as JSON.
+
+```c++
+std::ostream operator<<( std::ostream&, const coverage_result& );
+```
+
+The coverage information in the `coverage_result` is defined as follows.
+The `coverage_info` is used in two places, as part of the `coverage_entry` for each rule, and as value in the `branches` map for each immediate sub-rule.
+
+```c++
+struct coverage_info
+{
+   std::size_t start = 0;  // How often a rule was attempted to match.
+   std::size_t success = 0;  // How many attempts were a success (true).
+   std::size_t failure = 0;  // How many attempts were a local failure (false).
+   std::size_t unwind = 0;  // How many attempts were aborted due to an exception (thrown here or elsewhere).
+   std::size_t raise = 0;  // How many attempts were a global failure (exception thrown at this rule).
+};
+
+struct coverage_entry
+   : coverage_info  // The coverage_info for each rule.
+{
+   std::map< std::string_view, coverage_info > branches;  // The coverage_info for each immediate sub-rule.
+};
+
+struct coverage_result
+{
+   std::string_view grammar;  // Name of the top-level grammar rule.
+   std::string source;  // From the input.
+
+   std::map< std::string_view, coverage_entry > map;  // The coverage_entry for each rule.
+   bool result;  // Whether the parsing run was a success.
+};
+```
+
+As usual, unless otherwise indicated, all functions and data structure are in the namespace `tao::pegtl`.
 
 Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
diff --git a/packages/PEGTL/doc/Migration-Guide.md b/packages/PEGTL/doc/Migration-Guide.md
index ccbe64dad09a56632149765e2995260aa8574c18..2ef77f806dae469b588eb033c3b01e8671ee849a 100644
--- a/packages/PEGTL/doc/Migration-Guide.md
+++ b/packages/PEGTL/doc/Migration-Guide.md
@@ -4,13 +4,15 @@
 
 * The build system needs to be configured for C++17 or later.
 * The macro `TAO_PEGTL_NAMESPACE` now defines the fully qualified namespace and was changed from `pegtl` to `tao::pegtl`. When setting `TAO_PEGTL_NAMESPACE`, adapt as needed; in case of doubt, add `tao::` as prefix.
+* The 0-based `byte_in_line` was replaced with the 1-based `column`.
 * The control class template's `apply()` and `apply0()` must only be visible (e.g. via SFINAE) when an action class template function with the appropriate signature exists. See `tao::pegtl::normal` as example.
-* The error message from `tao::pegtl::parse_error` no longer contains the position as a prefix. The positions are available from the `positions` member variable.
 * If you have a use-case for [state](Rule-Reference.md#state-s-r-)'s `S::success()` to have an extended signature to get access to the current `apply_mode`, `rewind_mode`, *action*- and *control* class (template), please let us know and we'll re-add this feature.
 * The compatibility macros starting with `TAOCPP_PEGTL_` were removed, the corresponding `TAO_PEGTL_`-prefixed macros must be used.
 * The compatibility uppercase enumerators were removed, the corresponding lowercase enumerators must be used.
 * The compatibility `peek_byte()` member functions were removed, the `peek_uint8()` member functions must be used.
 * The compatibility header `changes.hpp` was removed, use the action-based `change_*.hpp` headers.
+* The parse tree nodes provide `node->type` (a `std::string_view`) instead of `node->name()`.
+* The parse tree nodes provide `node->string_view()` and `node->string()` instead of `node->content()`.
 
 ## Version 2.8.0
 
diff --git a/packages/PEGTL/doc/Parse-Tree.md b/packages/PEGTL/doc/Parse-Tree.md
index 59d5e6ec3f46da0f75f2375f0b15aadd052d1d91..f4a7f7c971296d87d00d18fde2ca18a1c46a4428 100644
--- a/packages/PEGTL/doc/Parse-Tree.md
+++ b/packages/PEGTL/doc/Parse-Tree.md
@@ -10,8 +10,6 @@ It provides the basic infrastructure to build a parse tree that
   * but can also be used with a custom tree node class that adheres to certain rules;
 * and supports on-the-fly tree transformations; some of the more common ones are included.
 
-> The parse tree / AST part of the PEGTL is currently in active development and serves as a prove-of-concept, expect changes at any time. Try it out, experiment with it, and most importantly let us know what you think of it. We need **your** feedback!
-
 ## Content
 
 * [Full Parse Tree](#full-parse-tree)
@@ -34,7 +32,7 @@ auto root = tao::pegtl::parse_tree::parse< my_grammar >( in );
 The result is a `std::unique_ptr< tao::pegtl::parse_tree::node >`.
 The pointer is empty when the input did not match the grammar, otherwise it contains the root node of the resulting parse tree.
 
-The tree nodes have a `name()` member function that returns the name of the grammar rule of which it represents a successful match, `begin()` and `end()` member functions to access the position of the matched portion of the input, `string()` and `string_view()` to actually access said matched input, and a vector called `children` with unique pointers to the child nodes.
+The tree nodes have a `type` member that contains the name of the grammar rule of which it represents a successful match, `begin()` and `end()` member functions to access the position of the matched portion of the input, `string()` and `string_view()` to actually access said matched input, and a vector called `children` with unique pointers to the child nodes.
 
 Note that the included tree node class **points** to the matched data, rather than copying it into the node, wherefore the input **must** still be "alive" when accessing the matched data!
 
diff --git a/packages/PEGTL/doc/README.md b/packages/PEGTL/doc/README.md
index 435f23193261a357715b02597dfa93a6c3148a3f..2fb1681fc3020828f0158ab0390b1c03415f77ba 100644
--- a/packages/PEGTL/doc/README.md
+++ b/packages/PEGTL/doc/README.md
@@ -45,6 +45,7 @@
     * [State Mismatch](Actions-and-States.md#state-mismatch)
   * [Legacy Actions](Actions-and-States.md#legacy-actions)
 * [Errors and Exceptions](Errors-and-Exceptions.md)
+  * [Global Failure]((Errors-and-Exceptions.md#global-failure)
   * [Local to Global Failure](Errors-and-Exceptions.md#local-to-global-failure)
     * [Intrusive Local to Global Failure](Errors-and-Exceptions.md#intrusive-local-to-global-failure)
     * [Non-Intrusive Local to Global Failure](Errors-and-Exceptions.md#non-intrusive-local-to-global-failure)
diff --git a/packages/PEGTL/doc/Rule-Reference.md b/packages/PEGTL/doc/Rule-Reference.md
index 8464355a5ed3125ce0bf02a2300cd52818e8eb1b..4072fd94462affe39a677fb2358ff49470b9b2f7 100644
--- a/packages/PEGTL/doc/Rule-Reference.md
+++ b/packages/PEGTL/doc/Rule-Reference.md
@@ -473,7 +473,7 @@ Note that the `true` template parameter to `internal::if_must` corresponds to th
 ###### `until< R, S... >`
 
 * Matches `seq< S... >` as long as `at< R >` does not match and succeeds when `R` matches.
-* [Equivalent] to `seq< star< not_at< R >, not_at< eof >, S... >, R >`.
+* [Equivalent] to `seq< star< not_at< R >, S... >, R >`.
 * Does not apply if `S` is an empty rule pack, see the previous entry for the semantics of `until< R >`.
 * [Meta data] and [implementation] mapping:
   - `until< R, S >::rule_t` is `internal::until< R, S >`
@@ -530,9 +530,9 @@ Atomic rules do not rely on other rules.
 
 ###### `bol`
 
-* Succeeds at "beginning-of-line", i.e. when the input's `byte_in_line()` member function returns zero.
+* Succeeds at "beginning-of-line", i.e. when the input's `column()` member function returns one.
 * Does not consume input.
-* Does **not** work with inputs that don't have a `byte_in_line()` member function.
+* Does **not** work with inputs that don't have a `column()` member function.
 * [Meta data] and [implementation] mapping:
   - `bol::rule_t` is `internal::bol`
 
diff --git a/packages/PEGTL/doc/Rules-and-Grammars.md b/packages/PEGTL/doc/Rules-and-Grammars.md
index 0fd0caa4c0a206832cc5baa886629277f686dec2..ece2da2dfc8269b4d95571f674c6957cf999dadf 100644
--- a/packages/PEGTL/doc/Rules-and-Grammars.md
+++ b/packages/PEGTL/doc/Rules-and-Grammars.md
@@ -71,44 +71,45 @@ Numbers are non-empty sequences of ASCII digits.
 The rule named `file` is the intended top-level rule of the grammar, i.e. the rule that is supplied as template argument to [the `parse()` function](Inputs-and-Parsing.md#parse-function) in order to start a parsing run with this grammar.
 
 ```c++
-using namespace tao::pegtl;
-
-struct line_comment
-   : until< eolf > {};
+struct hash_comment
+   : tao::pegtl::until< tao::pegtl::eolf > {};
 
 struct list;
 
 struct list_comment
-   : if_must< at< one< '(' > >, disable< list > > {};
+   : tao::pegtl::if_must< tao::pegtl::at< tao::pegtl::one< '(' > >, tao::pegtl::disable< list > > {};
 
-struct comment
-   : if_must< one< '#' >, sor< list_comment, line_comment > > {};
+struct read_include
+   : tao::pegtl::seq< tao::pegtl::one< ' ' >, tao::pegtl::one< '"' >, tao::pegtl::plus< tao::pegtl::not_one< '"' > >, tao::pegtl::one< '"' > > {};
 
-struct nothing
-   : sor< space, comment > {};
+struct hash_include
+   : tao::pegtl::if_must< tao::pegtl::string< 'i', 'n', 'c', 'l', 'u', 'd', 'e' >, read_include > {};
+
+struct hashed
+   : tao::pegtl::if_must< tao::pegtl::one< '#' >, tao::pegtl::sor< hash_include, list_comment, hash_comment > > {};
 
 struct number
-   : plus< digit > {};
+   : tao::pegtl::plus< tao::pegtl::digit > {};
 
 struct symbol
-   : identifier {};
+   : tao::pegtl::identifier {};
 
 struct atom
-   : sor< number, symbol > {};
+   : tao::pegtl::sor< number, symbol > {};
 
 struct anything;
 
 struct list
-   : if_must< one< '(' >, until< one< ')' >, anything > > {};
+   : tao::pegtl::if_must< tao::pegtl::one< '(' >, tao::pegtl::until< tao::pegtl::one< ')' >, anything > > {};
 
-struct something
-   : sor< atom, list > {};
+struct normal
+   : tao::pegtl::sor< atom, list > {};
 
 struct anything
-   : sor< nothing, something > {};
+   : tao::pegtl::sor< tao::pegtl::space, hashed, normal > {};
 
-struct file
-   : until< eof, anything > {};
+struct main
+   : tao::pegtl::until< tao::pegtl::eof, tao::pegtl::must< anything > > {};
 ```
 
 In order to let a parsing run do more than verify whether an input conforms to the grammar, it is necessary to attach user-defined *actions* to some grammar rules, as explained in [Actions and States](Actions-and-States.md).
diff --git a/packages/PEGTL/include/tao/pegtl/argv_input.hpp b/packages/PEGTL/include/tao/pegtl/argv_input.hpp
index 741a3574ddf2ab2de83afd677237f19382826512..311965e7f7bcdd24ede6d6fac5ab2084fd9e302d 100644
--- a/packages/PEGTL/include/tao/pegtl/argv_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/argv_input.hpp
@@ -42,7 +42,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   argv_input( Ts&&... )->argv_input<>;
+   argv_input( Ts&&... ) -> argv_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/ascii.hpp b/packages/PEGTL/include/tao/pegtl/ascii.hpp
index 1e4f1032f4287f4aac2c711c5659086c7666ad1d..e7948367c3a7ae6ff2272d51fd6390987c7a267d 100644
--- a/packages/PEGTL/include/tao/pegtl/ascii.hpp
+++ b/packages/PEGTL/include/tao/pegtl/ascii.hpp
@@ -15,8 +15,8 @@ namespace TAO_PEGTL_NAMESPACE
    inline namespace ascii
    {
       // clang-format off
-      struct alnum : internal::alnum {};
-      struct alpha : internal::alpha {};
+      struct alnum : internal::ranges< internal::peek_char, 'a', 'z', 'A', 'Z', '0', '9' > {};
+      struct alpha : internal::ranges< internal::peek_char, 'a', 'z', 'A', 'Z' > {};
       struct any : internal::any< internal::peek_char > {};
       struct blank : internal::one< internal::result_on_found::success, internal::peek_char, ' ', '\t' > {};
       struct digit : internal::range< internal::result_on_found::success, internal::peek_char, '0', '9' > {};
diff --git a/packages/PEGTL/include/tao/pegtl/buffer_input.hpp b/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
index f964787769d7f9ae5146ee3a29d99293ca1c556b..35637d145d05d996daeeff71d8ecbf320f44edc9 100644
--- a/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/buffer_input.hpp
@@ -96,9 +96,9 @@ namespace TAO_PEGTL_NAMESPACE
          return m_current.line;
       }
 
-      [[nodiscard]] std::size_t byte_in_line() const noexcept
+      [[nodiscard]] std::size_t column() const noexcept
       {
-         return m_current.byte_in_line;
+         return m_current.column;
       }
 
       [[nodiscard]] const Source& source() const noexcept
@@ -149,7 +149,7 @@ namespace TAO_PEGTL_NAMESPACE
          if( m_current.data + amount > m_buffer.get() + m_maximum ) {
             throw std::overflow_error( "require beyond end of buffer" );
          }
-         if( const auto r = m_reader( m_end, ( std::min )( buffer_free_after_end(), ( std::max )( amount, Chunk ) ) ) ) {
+         if( const auto r = m_reader( m_end, ( std::min )( buffer_free_after_end(), ( std::max )( amount - buffer_occupied(), Chunk ) ) ) ) {
             m_end += r;
          }
       }
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp b/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
index 91747dd90a37d0b9cf402c8992bd3d6d8b5efeff..4c8f39d201747eed92511b757d46e9bfdec15eec 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/alphabet.hpp
@@ -8,59 +8,59 @@
 
 namespace TAO_PEGTL_NAMESPACE::alphabet
 {
-   static const int a = 'a';
-   static const int b = 'b';
-   static const int c = 'c';
-   static const int d = 'd';
-   static const int e = 'e';
-   static const int f = 'f';
-   static const int g = 'g';
-   static const int h = 'h';
-   static const int i = 'i';
-   static const int j = 'j';
-   static const int k = 'k';
-   static const int l = 'l';
-   static const int m = 'm';
-   static const int n = 'n';
-   static const int o = 'o';
-   static const int p = 'p';
-   static const int q = 'q';
-   static const int r = 'r';
-   static const int s = 's';
-   static const int t = 't';
-   static const int u = 'u';
-   static const int v = 'v';
-   static const int w = 'w';
-   static const int x = 'x';
-   static const int y = 'y';
-   static const int z = 'z';
+   static const char a = 'a';
+   static const char b = 'b';
+   static const char c = 'c';
+   static const char d = 'd';
+   static const char e = 'e';
+   static const char f = 'f';
+   static const char g = 'g';
+   static const char h = 'h';
+   static const char i = 'i';
+   static const char j = 'j';
+   static const char k = 'k';
+   static const char l = 'l';
+   static const char m = 'm';
+   static const char n = 'n';
+   static const char o = 'o';
+   static const char p = 'p';
+   static const char q = 'q';
+   static const char r = 'r';
+   static const char s = 's';
+   static const char t = 't';
+   static const char u = 'u';
+   static const char v = 'v';
+   static const char w = 'w';
+   static const char x = 'x';
+   static const char y = 'y';
+   static const char z = 'z';
 
-   static const int A = 'A';  // NOLINT(readability-identifier-naming)
-   static const int B = 'B';  // NOLINT(readability-identifier-naming)
-   static const int C = 'C';  // NOLINT(readability-identifier-naming)
-   static const int D = 'D';  // NOLINT(readability-identifier-naming)
-   static const int E = 'E';  // NOLINT(readability-identifier-naming)
-   static const int F = 'F';  // NOLINT(readability-identifier-naming)
-   static const int G = 'G';  // NOLINT(readability-identifier-naming)
-   static const int H = 'H';  // NOLINT(readability-identifier-naming)
-   static const int I = 'I';  // NOLINT(readability-identifier-naming)
-   static const int J = 'J';  // NOLINT(readability-identifier-naming)
-   static const int K = 'K';  // NOLINT(readability-identifier-naming)
-   static const int L = 'L';  // NOLINT(readability-identifier-naming)
-   static const int M = 'M';  // NOLINT(readability-identifier-naming)
-   static const int N = 'N';  // NOLINT(readability-identifier-naming)
-   static const int O = 'O';  // NOLINT(readability-identifier-naming)
-   static const int P = 'P';  // NOLINT(readability-identifier-naming)
-   static const int Q = 'Q';  // NOLINT(readability-identifier-naming)
-   static const int R = 'R';  // NOLINT(readability-identifier-naming)
-   static const int S = 'S';  // NOLINT(readability-identifier-naming)
-   static const int T = 'T';  // NOLINT(readability-identifier-naming)
-   static const int U = 'U';  // NOLINT(readability-identifier-naming)
-   static const int V = 'V';  // NOLINT(readability-identifier-naming)
-   static const int W = 'W';  // NOLINT(readability-identifier-naming)
-   static const int X = 'X';  // NOLINT(readability-identifier-naming)
-   static const int Y = 'Y';  // NOLINT(readability-identifier-naming)
-   static const int Z = 'Z';  // NOLINT(readability-identifier-naming)
+   static const char A = 'A';  // NOLINT(readability-identifier-naming)
+   static const char B = 'B';  // NOLINT(readability-identifier-naming)
+   static const char C = 'C';  // NOLINT(readability-identifier-naming)
+   static const char D = 'D';  // NOLINT(readability-identifier-naming)
+   static const char E = 'E';  // NOLINT(readability-identifier-naming)
+   static const char F = 'F';  // NOLINT(readability-identifier-naming)
+   static const char G = 'G';  // NOLINT(readability-identifier-naming)
+   static const char H = 'H';  // NOLINT(readability-identifier-naming)
+   static const char I = 'I';  // NOLINT(readability-identifier-naming)
+   static const char J = 'J';  // NOLINT(readability-identifier-naming)
+   static const char K = 'K';  // NOLINT(readability-identifier-naming)
+   static const char L = 'L';  // NOLINT(readability-identifier-naming)
+   static const char M = 'M';  // NOLINT(readability-identifier-naming)
+   static const char N = 'N';  // NOLINT(readability-identifier-naming)
+   static const char O = 'O';  // NOLINT(readability-identifier-naming)
+   static const char P = 'P';  // NOLINT(readability-identifier-naming)
+   static const char Q = 'Q';  // NOLINT(readability-identifier-naming)
+   static const char R = 'R';  // NOLINT(readability-identifier-naming)
+   static const char S = 'S';  // NOLINT(readability-identifier-naming)
+   static const char T = 'T';  // NOLINT(readability-identifier-naming)
+   static const char U = 'U';  // NOLINT(readability-identifier-naming)
+   static const char V = 'V';  // NOLINT(readability-identifier-naming)
+   static const char W = 'W';  // NOLINT(readability-identifier-naming)
+   static const char X = 'X';  // NOLINT(readability-identifier-naming)
+   static const char Y = 'Y';  // NOLINT(readability-identifier-naming)
+   static const char Z = 'Z';  // NOLINT(readability-identifier-naming)
 
 }  // namespace TAO_PEGTL_NAMESPACE::alphabet
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp b/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
index 128017d0f9397aa2b0c29f32398d102b9787fcfb..b777c02bc86961c27e4f80da06d9bc9028187e84 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/analyze.hpp
@@ -15,10 +15,12 @@
 #include <vector>
 
 #include "../config.hpp"
+#include "../demangle.hpp"
 
 #include "analyze_traits.hpp"
 
-#include "../internal/demangle.hpp"
+#include "internal/set_stack_guard.hpp"
+
 #include "../internal/dependent_false.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
@@ -35,41 +37,6 @@ namespace TAO_PEGTL_NAMESPACE
          std::vector< std::string_view > subs;
       };
 
-      template< typename C >
-      class analyze_guard
-      {
-      public:
-         analyze_guard( C& container, const typename C::value_type& value )
-            : m_i( container.insert( value ) ),
-              m_c( container )
-         {}
-
-         analyze_guard( analyze_guard&& ) = delete;
-         analyze_guard( const analyze_guard& ) = delete;
-
-         void operator=( analyze_guard&& ) = delete;
-         void operator=( const analyze_guard& ) = delete;
-
-         ~analyze_guard()
-         {
-            if( m_i.second ) {
-               m_c.erase( m_i.first );
-            }
-         }
-
-         explicit operator bool() const noexcept
-         {
-            return m_i.second;
-         }
-
-      private:
-         const std::pair< typename C::iterator, bool > m_i;
-         C& m_c;
-      };
-
-      template< typename C >
-      analyze_guard( C&, const typename C::value_type& )->analyze_guard< C >;
-
       class analyze_cycles_impl
       {
       public:
@@ -114,7 +81,7 @@ namespace TAO_PEGTL_NAMESPACE
             if( const auto j = m_cache.find( start->first ); j != m_cache.end() ) {
                return j->second;
             }
-            if( const auto g = analyze_guard( m_stack, start->first ) ) {
+            if( const auto g = set_stack_guard( m_stack, start->first ) ) {
                switch( start->second.type ) {
                   case analyze_type::any: {
                      bool a = false;
@@ -150,7 +117,7 @@ namespace TAO_PEGTL_NAMESPACE
             if( !accum ) {
                ++m_problems;
                if( m_verbose ) {
-                  std::cout << "problem: cycle without progress detected at rule class " << start->first << std::endl;  // LCOV_EXCL_LINE
+                  std::cerr << "problem: cycle without progress detected at rule class " << start->first << std::endl;  // LCOV_EXCL_LINE
                }
             }
             return m_cache[ start->first ] = accum;
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp b/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c716c66d69c2f1f75ad01bedadfd0073a43c196d
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/control_action.hpp
@@ -0,0 +1,77 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_CONTRIB_CONTROL_ACTION_HPP
+#define TAO_PEGTL_CONTRIB_CONTROL_ACTION_HPP
+
+#include <utility>
+
+#include "../config.hpp"
+#include "../match.hpp"
+#include "../nothing.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   namespace internal
+   {
+      template< typename, typename Rule, template< typename... > class Action, typename ParseInput, typename... States >
+      inline constexpr bool action_has_unwind = false;
+
+      template< typename Rule, template< typename... > class Action, typename ParseInput, typename... States >
+      inline constexpr bool action_has_unwind< decltype( (void)Action< Rule >::unwind( std::declval< const ParseInput& >(), std::declval< States&& >()... ) ), Rule, Action, ParseInput, States... > = true;
+
+   }  // namespace internal
+
+   struct control_action
+      : maybe_nothing
+   {
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
+      {
+         if constexpr( internal::action_has_unwind< void, Rule, Action, ParseInput, States... > ) {
+            try {
+               return control_action::match_impl< Rule, A, M, Action, Control >( in, st... );
+            }
+            catch( ... ) {
+               Action< Rule >::unwind( const_cast< const ParseInput& >( in ), st... );
+               throw;
+            }
+         }
+         else {
+            return control_action::match_impl< Rule, A, M, Action, Control >( in, st... );
+         }
+      }
+
+   private:
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static bool match_impl( ParseInput& in, States&&... st )
+      {
+         Action< Rule >::start( const_cast< const ParseInput& >( in ), st... );
+         if( TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, st... ) ) {
+            Action< Rule >::success( const_cast< const ParseInput& >( in ), st... );
+            return true;
+         }
+         Action< Rule >::failure( const_cast< const ParseInput& >( in ), st... );
+         return false;
+      }
+   };
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp b/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
index 582813a8c6de1a450eeb3cb5770fab60d1bd05c1..78e9c64d167258337a3c039c85f051f8c11bd7ce 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/coverage.hpp
@@ -4,33 +4,31 @@
 #ifndef TAO_PEGTL_CONTRIB_COVERAGE_HPP
 #define TAO_PEGTL_CONTRIB_COVERAGE_HPP
 
-#include <cassert>
 #include <cstddef>
 #include <map>
-#include <string>
 #include <string_view>
 #include <vector>
 
-#include "remove_first_state.hpp"
-#include "shuffle_states.hpp"
+#include "state_control.hpp"
 
+#include "../apply_mode.hpp"
 #include "../config.hpp"
+#include "../demangle.hpp"
 #include "../normal.hpp"
 #include "../nothing.hpp"
 #include "../parse.hpp"
+#include "../rewind_mode.hpp"
 #include "../type_list.hpp"
 #include "../visit.hpp"
 
-#include "../internal/demangle.hpp"
-
 namespace TAO_PEGTL_NAMESPACE
 {
    struct coverage_info
    {
       std::size_t start = 0;
       std::size_t success = 0;
-      std::size_t local_failure = 0;
-      std::size_t global_failure = 0;
+      std::size_t failure = 0;
+      std::size_t unwind = 0;
       std::size_t raise = 0;
    };
 
@@ -40,94 +38,98 @@ namespace TAO_PEGTL_NAMESPACE
       std::map< std::string_view, coverage_info > branches;
    };
 
-   struct coverage_state
-   {
-      std::string_view grammar;
-      std::string source;
-
-      std::map< std::string_view, coverage_entry > map;
-      bool result;
-
-      std::vector< std::string_view > stack;
-   };
+   using coverage_result = std::map< std::string_view, coverage_entry >;
 
    namespace internal
    {
       template< typename Rule >
       struct coverage_insert
       {
-         static void visit( coverage_state& state )
+         static void visit( std::map< std::string_view, coverage_entry >& map )
          {
-            visit_branches( state.map.try_emplace( internal::demangle< Rule >() ).first->second.branches, typename Rule::subs_t() );
+            visit_branches( map.try_emplace( demangle< Rule >() ).first->second.branches, typename Rule::subs_t() );
          }
 
          template< typename... Ts >
          static void visit_branches( std::map< std::string_view, coverage_info >& branches, type_list< Ts... > /*unused*/ )
          {
-            ( branches.try_emplace( internal::demangle< Ts >() ), ... );
+            ( branches.try_emplace( demangle< Ts >() ), ... );
          }
       };
 
-      template< template< typename... > class Control = normal >
-      struct make_coverage_control
+      struct coverage_state
       {
          template< typename Rule >
-         struct control
-            : remove_first_state< Control< Rule > >
+         static constexpr bool enable = true;
+
+         explicit coverage_state( coverage_result& in_result )
+            : result( in_result )
+         {}
+
+         coverage_result& result;
+         std::vector< std::string_view > stack;
+
+         template< typename Rule, typename ParseInput, typename... States >
+         void start( const ParseInput& /*unused*/, States&&... /*unused*/ )
          {
-            template< typename ParseInput, typename... States >
-            [[noreturn]] static void raise( const ParseInput& in, coverage_state& state, States&&... st )
-            {
-               const auto name = internal::demangle< Rule >();
-               ++state.map.at( name ).raise;
-               if( state.stack.size() > 1 ) {
-                  ++state.map.at( state.stack.at( state.stack.size() - 2 ) ).branches.at( name ).raise;
-               }
-               Control< Rule >::raise( in, st... );
+            const auto name = demangle< Rule >();
+            ++result.at( name ).start;
+            if( !stack.empty() ) {
+               ++result.at( stack.back() ).branches.at( name ).start;
             }
+            stack.push_back( name );
+         }
 
-            template< apply_mode A,
-                      rewind_mode M,
-                      template< typename... >
-                      class Action,
-                      template< typename... >
-                      class Control2,
-                      typename ParseInput,
-                      typename... States >
-            [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-            {
-               coverage_entry dummy;
-               auto& state = std::get< sizeof...( st ) - 1 >( std::tie( st... ) );
-               const auto name = internal::demangle< Rule >();
-               auto& entry = state.map.at( name );
-               auto& previous = state.stack.empty() ? dummy : state.map.at( state.stack.back() ).branches.at( name );
-               ++entry.start;
-               ++previous.start;
-               state.stack.push_back( name );
-               try {
-                  const bool result = Control< Rule >::template match< A, M, Action, Control2 >( in, st... );
-                  state.stack.pop_back();
-                  if( result ) {
-                     ++entry.success;
-                     ++previous.success;
-                  }
-                  else {
-                     ++entry.local_failure;
-                     ++previous.local_failure;
-                  }
-                  return result;
-               }
-               catch( ... ) {
-                  state.stack.pop_back();
-                  ++entry.global_failure;
-                  ++previous.global_failure;
-                  throw;
-               }
+         template< typename Rule, typename ParseInput, typename... States >
+         void success( const ParseInput& /*unused*/, States&&... /*unused*/ )
+         {
+            stack.pop_back();
+            const auto name = demangle< Rule >();
+            ++result.at( name ).success;
+            if( !stack.empty() ) {
+               ++result.at( stack.back() ).branches.at( name ).success;
             }
-         };
+         }
 
-         template< typename Rule >
-         using type = rotate_states_right< control< Rule > >;
+         template< typename Rule, typename ParseInput, typename... States >
+         void failure( const ParseInput& /*unused*/, States&&... /*unused*/ )
+         {
+            stack.pop_back();
+            const auto name = demangle< Rule >();
+            ++result.at( name ).failure;
+            if( !stack.empty() ) {
+               ++result.at( stack.back() ).branches.at( name ).failure;
+            }
+         }
+
+         template< typename Rule, typename ParseInput, typename... States >
+         void raise( const ParseInput& /*unused*/, States&&... /*unused*/ )
+         {
+            const auto name = demangle< Rule >();
+            ++result.at( name ).raise;
+            if( !stack.empty() ) {
+               ++result.at( stack.back() ).branches.at( name ).raise;
+            }
+         }
+
+         template< typename Rule, typename ParseInput, typename... States >
+         void unwind( const ParseInput& /*unused*/, States&&... /*unused*/ )
+         {
+            stack.pop_back();
+            const auto name = demangle< Rule >();
+            ++result.at( name ).unwind;
+            if( !stack.empty() ) {
+               ++result.at( stack.back() ).branches.at( name ).unwind;
+            }
+         }
+
+         template< typename Rule, typename ParseInput, typename... States >
+         void apply( const ParseInput& /*unused*/, States&&... /*unused*/ ) noexcept
+         {}
+
+         template< typename Rule, typename ParseInput, typename... States >
+         void apply0( const ParseInput& /*unused*/, States&&... /*unused*/ ) noexcept
+         {}
       };
 
    }  // namespace internal
@@ -137,21 +139,11 @@ namespace TAO_PEGTL_NAMESPACE
              template< typename... > class Control = normal,
              typename ParseInput,
              typename... States >
-   coverage_state coverage( ParseInput&& in, States&&... st )
+   bool coverage( ParseInput&& in, coverage_result& result, States&&... st )
    {
-      coverage_state state;
-
-      state.grammar = internal::demangle< Rule >();
-      state.source = in.source();
-
-      // populate state
-      visit< Rule, internal::coverage_insert >( state );
-
-      // parse
-      state.result = parse< Rule, Action, internal::make_coverage_control<>::template type >( in, st..., state );
-      assert( state.stack.empty() );
-
-      return state;
+      internal::coverage_state state( result );
+      visit< Rule, internal::coverage_insert >( state.result );  // Fill map with all sub-rules of the grammar.
+      return parse< Rule, Action, state_control< Control >::template type >( in, st..., state );
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp b/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
index 9bbbf9977be4e39782f869ee288758fe33b34157..30ba5464faa0961f5c94e78680960b4eac891a5c 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/integer.hpp
@@ -7,6 +7,8 @@
 #include <cstdint>
 #include <cstdlib>
 
+#include <limits>
+#include <string_view>
 #include <type_traits>
 
 #include "../ascii.hpp"
@@ -148,7 +150,7 @@ namespace TAO_PEGTL_NAMESPACE
             if( is_digit( c ) ) {
                in.bump_in_this_line();
                if( c == '0' ) {
-                  return in.empty() || ( !is_digit( in.peek_char() ) );  // TODO: Throw exception on digit?
+                  return in.empty() || ( !is_digit( in.peek_char() ) );
                }
                while( ( !in.empty() ) && is_digit( in.peek_char() ) ) {
                   in.bump_in_this_line();
@@ -171,11 +173,11 @@ namespace TAO_PEGTL_NAMESPACE
             if( is_digit( c ) ) {
                if( c == '0' ) {
                   in.bump_in_this_line();
-                  return in.empty() || ( !is_digit( in.peek_char() ) );  // TODO: Throw exception on digit?
+                  return in.empty() || ( !is_digit( in.peek_char() ) );
                }
                do {
                   if( !accumulate_digit< Unsigned, Maximum >( st, c ) ) {
-                     throw parse_error( "integer overflow", in );  // Consistent with "as if" an action was doing the conversion.
+                     throw TAO_PEGTL_NAMESPACE::parse_error( "integer overflow", in );  // Consistent with "as if" an action was doing the conversion.
                   }
                   in.bump_in_this_line();
                } while( ( !in.empty() ) && is_digit( c = in.peek_char() ) );
@@ -192,7 +194,7 @@ namespace TAO_PEGTL_NAMESPACE
       // Assumes that 'in' contains a non-empty sequence of ASCII digits.
 
       template< typename ActionInput, typename Unsigned >
-      static auto apply( const ActionInput& in, Unsigned& st ) -> std::enable_if_t< std::is_unsigned_v< Unsigned >, void >
+      static void apply( const ActionInput& in, Unsigned& st )
       {
          // This function "only" offers basic exception safety.
          st = 0;
@@ -200,20 +202,6 @@ namespace TAO_PEGTL_NAMESPACE
             throw parse_error( "unsigned integer overflow", in );
          }
       }
-
-      template< typename ActionInput, typename State >
-      static auto apply( const ActionInput& in, State& st ) -> std::enable_if_t< std::is_class_v< State >, void >
-      {
-         apply( in, st.converted );  // Compatibility for pre-3.0 behaviour.
-      }
-
-      template< typename ActionInput, typename Unsigned, typename... Ts >
-      static auto apply( const ActionInput& in, std::vector< Unsigned, Ts... >& st ) -> std::enable_if_t< std::is_unsigned_v< Unsigned >, void >
-      {
-         Unsigned u = 0;
-         apply( in, u );
-         st.emplace_back( u );
-      }
    };
 
    struct unsigned_rule
@@ -260,9 +248,6 @@ namespace TAO_PEGTL_NAMESPACE
          st = 0;
          return internal::match_and_convert_unsigned_with_maximum( in, st );  // Throws on overflow.
       }
-
-      // TODO: Overload for st.converted?
-      // TODO: Overload for std::vector< Unsigned >?
    };
 
    template< typename Unsigned, Unsigned Maximum >
@@ -273,7 +258,7 @@ namespace TAO_PEGTL_NAMESPACE
       static_assert( std::is_unsigned_v< Unsigned > );
 
       template< typename ActionInput, typename Unsigned2 >
-      static auto apply( const ActionInput& in, Unsigned2& st ) -> std::enable_if_t< std::is_same_v< Unsigned, Unsigned2 >, void >
+      static void apply( const ActionInput& in, Unsigned2& st )
       {
          // This function "only" offers basic exception safety.
          st = 0;
@@ -281,20 +266,6 @@ namespace TAO_PEGTL_NAMESPACE
             throw parse_error( "unsigned integer overflow", in );
          }
       }
-
-      template< typename ActionInput, typename State >
-      static auto apply( const ActionInput& in, State& st ) -> std::enable_if_t< std::is_class_v< State >, void >
-      {
-         apply( in, st.converted );  // Compatibility for pre-3.0 behaviour.
-      }
-
-      template< typename ActionInput, typename Unsigned2, typename... Ts >
-      static auto apply( const ActionInput& in, std::vector< Unsigned2, Ts... >& st ) -> std::enable_if_t< std::is_same_v< Unsigned, Unsigned2 >, void >
-      {
-         Unsigned u = 0;
-         apply( in, u );
-         st.emplace_back( u );
-      }
    };
 
    template< typename Unsigned, Unsigned Maximum = ( std::numeric_limits< Unsigned >::max )() >
@@ -349,9 +320,6 @@ namespace TAO_PEGTL_NAMESPACE
          st = 0;
          return internal::match_and_convert_unsigned_with_maximum< ParseInput, Unsigned, Maximum >( in, st );  // Throws on overflow.
       }
-
-      // TODO: Overload for st.converted?
-      // TODO: Overload for std::vector< Unsigned >?
    };
 
    struct signed_action
@@ -360,7 +328,7 @@ namespace TAO_PEGTL_NAMESPACE
       // with optional leading sign; with sign, in.size() must be >= 2.
 
       template< typename ActionInput, typename Signed >
-      static auto apply( const ActionInput& in, Signed& st ) -> std::enable_if_t< std::is_signed_v< Signed >, void >
+      static void apply( const ActionInput& in, Signed& st )
       {
          // This function "only" offers basic exception safety.
          st = 0;
@@ -368,20 +336,6 @@ namespace TAO_PEGTL_NAMESPACE
             throw parse_error( "signed integer overflow", in );
          }
       }
-
-      template< typename ActionInput, typename State >
-      static auto apply( const ActionInput& in, State& st ) -> std::enable_if_t< std::is_class_v< State >, void >
-      {
-         apply( in, st.converted );  // Compatibility for pre-3.0 behaviour.
-      }
-
-      template< typename ActionInput, typename Signed, typename... Ts >
-      static auto apply( const ActionInput& in, std::vector< Signed, Ts... >& st ) -> std::enable_if_t< std::is_signed_v< Signed >, void >
-      {
-         Signed s = 0;
-         apply( in, s );
-         st.emplace_back( s );
-      }
    };
 
    struct signed_rule
@@ -440,9 +394,6 @@ namespace TAO_PEGTL_NAMESPACE
       {
          return TAO_PEGTL_NAMESPACE::parse< signed_rule_new, internal::signed_action_action >( in, st );  // Throws on overflow.
       }
-
-      // TODO: Overload for st.converted?
-      // TODO: Overload for std::vector< Signed >?
    };
 
    template< typename Name >
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp b/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..99870f9b04d70fceb0a7785f1344d3b4d5555cf1
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/internal/set_stack_guard.hpp
@@ -0,0 +1,52 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_CONTRIB_INTERNAL_SET_STACK_GUARD_HPP
+#define TAO_PEGTL_CONTRIB_INTERNAL_SET_STACK_GUARD_HPP
+
+#include <set>
+#include <utility>
+
+#include "../../config.hpp"
+
+namespace TAO_PEGTL_NAMESPACE::internal
+{
+   template< typename... Cs >
+   class set_stack_guard
+   {
+   public:
+      template< typename... Ts >
+      set_stack_guard( std::set< Cs... >& set, Ts&&... ts )
+         : m_i( set.emplace( std::forward< Ts >( ts )... ) ),
+           m_s( set )
+      {}
+
+      set_stack_guard( set_stack_guard&& ) = delete;
+      set_stack_guard( const set_stack_guard& ) = delete;
+
+      void operator=( set_stack_guard&& ) = delete;
+      void operator=( const set_stack_guard& ) = delete;
+
+      ~set_stack_guard()
+      {
+         if( m_i.second ) {
+            m_s.erase( m_i.first );
+         }
+      }
+
+      explicit operator bool() const noexcept
+      {
+         return m_i.second;
+      }
+
+   private:
+      const std::pair< typename std::set< Cs... >::iterator, bool > m_i;
+      std::set< Cs... >& m_s;
+   };
+
+   template< typename... Cs >
+   set_stack_guard( std::set< Cs... >&, const typename std::set< Cs... >::value_type& ) -> set_stack_guard< Cs... >;
+
+}  // namespace TAO_PEGTL_NAMESPACE::internal
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
index 964fdf6f0ec8ef74e98c58cc29d87d69c44eb4ee..25742807037cd2fb5cc1288da7c7b7ce45de8f0b 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/parse_tree.hpp
@@ -19,16 +19,16 @@
 
 #include "../apply_mode.hpp"
 #include "../config.hpp"
+#include "../demangle.hpp"
 #include "../memory_input.hpp"
 #include "../normal.hpp"
 #include "../nothing.hpp"
 #include "../parse.hpp"
 #include "../rewind_mode.hpp"
 
-#include "../internal/demangle.hpp"
 #include "../internal/enable_control.hpp"
+#include "../internal/has_unwind.hpp"
 #include "../internal/iterator.hpp"
-#include "../internal/try_catch_type.hpp"
 
 namespace TAO_PEGTL_NAMESPACE::parse_tree
 {
@@ -67,13 +67,13 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
       template< typename U >
       [[nodiscard]] bool is_type() const noexcept
       {
-         return type == TAO_PEGTL_NAMESPACE::internal::demangle< U >();
+         return type == demangle< U >();
       }
 
       template< typename U >
       void set_type() noexcept
       {
-         type = TAO_PEGTL_NAMESPACE::internal::demangle< U >();
+         type = demangle< U >();
       }
 
       [[nodiscard]] position begin() const
@@ -107,13 +107,13 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
       [[nodiscard]] memory_input< P, Eol > as_memory_input() const
       {
          assert( has_content() );
-         return { m_begin.data, m_end.data, source, m_begin.byte, m_begin.line, m_begin.byte_in_line };
+         return { m_begin.data, m_end.data, source, m_begin.byte, m_begin.line, m_begin.column };
       }
 
       template< typename... States >
       void remove_content( States&&... /*unused*/ ) noexcept
       {
-         m_end.reset();
+         m_end = TAO_PEGTL_NAMESPACE::internal::iterator();
       }
 
       // all non-root nodes are initialized by calling this method
@@ -137,6 +137,11 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
       void failure( const ParseInput& /*unused*/, States&&... /*unused*/ ) noexcept
       {}
 
+      // if parsing of the rule failed with an exception, this method is called
+      template< typename Rule, typename ParseInput, typename... States >
+      void unwind( const ParseInput& /*unused*/, States&&... /*unused*/ ) noexcept
+      {}
+
       // if parsing succeeded and the (optional) transform call
       // did not discard the node, it is appended to its parent.
       // note that "child" is the node whose Rule just succeeded
@@ -155,12 +160,6 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
 
    namespace internal
    {
-      template< typename >
-      inline constexpr bool is_try_catch_type = false;
-
-      template< typename Exception, typename... Rules >
-      inline constexpr bool is_try_catch_type< TAO_PEGTL_NAMESPACE::internal::try_catch_type< Exception, Rules... > > = true;
-
       template< typename Node >
       struct state
       {
@@ -195,14 +194,14 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
 
       template< typename Selector, typename ParseInput, typename Node, typename... States >
       auto transform( const ParseInput& in, std::unique_ptr< Node >& n, States&&... st ) noexcept( noexcept( Selector::transform( in, n, st... ) ) )
-         -> decltype( Selector::transform( in, n, st... ), void() )
+         -> decltype( (void)Selector::transform( in, n, st... ) )
       {
          Selector::transform( in, n, st... );
       }
 
       template< typename Selector, typename ParseInput, typename Node, typename... States >
       auto transform( const ParseInput& /*unused*/, std::unique_ptr< Node >& n, States&&... st ) noexcept( noexcept( Selector::transform( n, st... ) ) )
-         -> decltype( Selector::transform( n, st... ), void() )
+         -> decltype( (void)Selector::transform( n, st... ) )
       {
          Selector::transform( n, st... );
       }
@@ -243,46 +242,33 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
       struct make_control< Node, Selector, Control >::state_handler< Rule, false, false >
          : remove_first_state< Control< Rule > >
       {
-         template< apply_mode A,
-                   rewind_mode M,
-                   template< typename... >
-                   class Action,
-                   template< typename... >
-                   class Control2,
-                   typename ParseInput,
-                   typename... States >
-         [[nodiscard]] static bool match( ParseInput& in, States&&... st )
+         template< typename ParseInput, typename... States >
+         static void start( const ParseInput& /*unused*/, state< Node >& state, States&&... /*unused*/ )
          {
-            auto& state = std::get< sizeof...( st ) - 1 >( std::tie( st... ) );
-            if constexpr( is_try_catch_type< Rule > ) {
-               internal::state< Node > tmp;
-               tmp.emplace_back();
-               tmp.stack.swap( state.stack );
-               const bool result = Control< Rule >::template match< A, M, Action, Control2 >( in, st... );
-               tmp.stack.swap( state.stack );
-               if( result ) {
-                  for( auto& c : tmp.back()->children ) {
-                     state.back()->children.emplace_back( std::move( c ) );
-                  }
-               }
-               return result;
-            }
-            else {
-               state.emplace_back();
-               const bool result = Control< Rule >::template match< A, M, Action, Control2 >( in, st... );
-               if( result ) {
-                  auto n = std::move( state.back() );
-                  state.pop_back();
-                  for( auto& c : n->children ) {
-                     state.back()->children.emplace_back( std::move( c ) );
-                  }
-               }
-               else {
-                  state.pop_back();
-               }
-               return result;
+            state.emplace_back();
+         }
+
+         template< typename ParseInput, typename... States >
+         static void success( const ParseInput& /*unused*/, state< Node >& state, States&&... /*unused*/ )
+         {
+            auto n = std::move( state.back() );
+            state.pop_back();
+            for( auto& c : n->children ) {
+               state.back()->children.emplace_back( std::move( c ) );
             }
          }
+
+         template< typename ParseInput, typename... States >
+         static void failure( const ParseInput& /*unused*/, state< Node >& state, States&&... /*unused*/ )
+         {
+            state.pop_back();
+         }
+
+         template< typename ParseInput, typename... States >
+         static void unwind( const ParseInput& /*unused*/, state< Node >& state, States&&... /*unused*/ )
+         {
+            state.pop_back();
+         }
       };
 
       template< typename Node, template< typename... > class Selector, template< typename... > class Control >
@@ -301,7 +287,6 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
          template< typename ParseInput, typename... States >
          static void success( const ParseInput& in, state< Node >& state, States&&... st )
          {
-            Control< Rule >::success( in, st... );
             auto n = std::move( state.back() );
             state.pop_back();
             n->template success< Rule >( in, st... );
@@ -309,14 +294,25 @@ namespace TAO_PEGTL_NAMESPACE::parse_tree
             if( n ) {
                state.back()->emplace_back( std::move( n ), st... );
             }
+            Control< Rule >::success( in, st... );
          }
 
          template< typename ParseInput, typename... States >
-         static void failure( const ParseInput& in, state< Node >& state, States&&... st ) noexcept( noexcept( Control< Rule >::failure( in, st... ) ) && noexcept( std::declval< Node& >().template failure< Rule >( in, st... ) ) )
+         static void failure( const ParseInput& in, state< Node >& state, States&&... st )
          {
-            Control< Rule >::failure( in, st... );
             state.back()->template failure< Rule >( in, st... );
             state.pop_back();
+            Control< Rule >::failure( in, st... );
+         }
+
+         template< typename ParseInput, typename... States >
+         static void unwind( const ParseInput& in, state< Node >& state, States&&... st )
+         {
+            state.back()->template unwind< Rule >( in, st... );
+            state.pop_back();
+            if constexpr( TAO_PEGTL_NAMESPACE::internal::has_unwind< Control< Rule >, void, const ParseInput&, States... > ) {
+               Control< Rule >::unwind( in, st... );
+            }
          }
       };
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/print.hpp b/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
index 96c1792fe91d6798ea9b2ed267c3ccdc33c7d57c..cc4ce4857460945ebb382e4722cb61b29f62933b 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/print.hpp
@@ -7,16 +7,16 @@
 #include <ostream>
 
 #include "../config.hpp"
+#include "../demangle.hpp"
+#include "../type_list.hpp"
 #include "../visit.hpp"
 
-#include "../internal/demangle.hpp"
-
 namespace TAO_PEGTL_NAMESPACE
 {
    namespace internal
    {
       template< typename Name >
-      struct print_rules
+      struct print_names
       {
          static void visit( std::ostream& os )
          {
@@ -25,7 +25,7 @@ namespace TAO_PEGTL_NAMESPACE
       };
 
       template< typename Name >
-      struct print_sub_rules
+      struct print_debug
       {
          static void visit( std::ostream& os )
          {
@@ -59,15 +59,15 @@ namespace TAO_PEGTL_NAMESPACE
    }  // namespace internal
 
    template< typename Grammar >
-   void print_rules( std::ostream& os )
+   void print_names( std::ostream& os )
    {
-      visit< Grammar, internal::print_rules >( os );
+      visit< Grammar, internal::print_names >( os );
    }
 
    template< typename Grammar >
-   void print_sub_rules( std::ostream& os )
+   void print_debug( std::ostream& os )
    {
-      visit< Grammar, internal::print_sub_rules >( os );
+      visit< Grammar, internal::print_debug >( os );
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp b/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
index 0c49af45e70214c29d2fd7d1b48f489d96d3e9c7..f3ab737e8f1084e8f1920a5b78a9c048ce0e5dea 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/print_coverage.hpp
@@ -10,31 +10,25 @@
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   // output is JSON
-   inline void print_coverage( std::ostream& os, const coverage_state& state )
+   inline std::ostream& operator<<( std::ostream& os, const coverage_result& result )
    {
-      os << "{\n"
-         << "  \"grammar\": \"" << state.grammar << "\",\n"
-         << "  \"source\": \"" << state.source << "\",\n"
-         << "  \"result\": " << ( state.result ? "true" : "false" ) << ",\n"
-         << "  \"coverage\":\n"
-         << "  [\n";
+      os << "[\n";
       bool f = true;
-      for( const auto& [ k, v ] : state.map ) {
+      for( const auto& [ k, v ] : result ) {
          if( f ) {
             f = false;
          }
          else {
             os << ",\n";
          }
-         os << "    {\n"
-            << "      \"rule\": \"" << k << "\",\n"
-            << "      \"start\": " << v.start << ", \"success\": " << v.success << ", \"local_failure\": " << v.local_failure << ", \"global_failure\": " << v.global_failure << ", \"raise\": " << v.raise << ",\n";
+         os << "  {\n"
+            << "    \"rule\": \"" << k << "\",\n"
+            << "    \"start\": " << v.start << ", \"success\": " << v.success << ", \"failure\": " << v.failure << ", \"unwind\": " << v.unwind << ", \"raise\": " << v.raise << ",\n";
          if( v.branches.empty() ) {
-            os << "      \"branches\": []\n";
+            os << "    \"branches\": []\n";
          }
          else {
-            os << "      \"branches\": [\n";
+            os << "    \"branches\": [\n";
             bool f2 = true;
             for( const auto& [ k2, v2 ] : v.branches ) {
                if( f2 ) {
@@ -43,15 +37,15 @@ namespace TAO_PEGTL_NAMESPACE
                else {
                   os << ",\n";
                }
-               os << "        { \"branch\": \"" << k2 << "\", \"start\": " << v2.start << ", \"success\": " << v2.success << ", \"local_failure\": " << v2.local_failure << ", \"global_failure\": " << v2.global_failure << ", \"raise\": " << v2.raise << " }";
+               os << "      { \"branch\": \"" << k2 << "\", \"start\": " << v2.start << ", \"success\": " << v2.success << ", \"failure\": " << v2.failure << ", \"unwind\": " << v2.unwind << ", \"raise\": " << v2.raise << " }";
             }
-            os << "\n      ]\n";
+            os << "\n    ]\n";
          }
-         os << "    }";
+         os << "  }";
       }
-      os << "\n"
-         << "  ]\n"
-         << "}\n";
+      os << "\n";
+      os << "]\n";
+      return os;
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp b/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
index 7fc2e049bd994aa7ceb44a2e7bccb775c3d48331..3e4a6a7f4b561d25e1640879402738bc107f7a1e 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/raw_string.hpp
@@ -11,15 +11,7 @@
 #include "../ascii.hpp"
 #include "../config.hpp"
 #include "../rewind_mode.hpp"
-
-#include "../internal/bytes.hpp"
-#include "../internal/enable_control.hpp"
-#include "../internal/eof.hpp"
-#include "../internal/eol.hpp"
-#include "../internal/must.hpp"
-#include "../internal/not_at.hpp"
-#include "../internal/seq.hpp"
-#include "../internal/star.hpp"
+#include "../rules.hpp"
 
 #include "analyze_traits.hpp"
 
@@ -204,7 +196,7 @@ namespace TAO_PEGTL_NAMESPACE
       {};
 
       using rule_t = raw_string;
-      using subs_t = type_list< internal::raw_string_open< Open, Marker >, internal::must< content > >;
+      using subs_t = empty_list;  // type_list< internal::raw_string_open< Open, Marker >, internal::must< content > >;
 
       template< apply_mode A,
                 rewind_mode M,
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp b/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
index 48030def2e9bb687e86783ce0558029fd43ad96c..755b913498edbc776be1f85699cc7765e26ec7e0 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/remove_first_state.hpp
@@ -4,8 +4,12 @@
 #ifndef TAO_PEGTL_CONTRIB_REMOVE_FIRST_STATE_HPP
 #define TAO_PEGTL_CONTRIB_REMOVE_FIRST_STATE_HPP
 
+#include <type_traits>
+
 #include "../config.hpp"
 
+#include "../internal/has_unwind.hpp"
+
 namespace TAO_PEGTL_NAMESPACE
 {
    // Applies to start(), success(), failure(), raise(), apply(), and apply0():
@@ -38,6 +42,13 @@ namespace TAO_PEGTL_NAMESPACE
          Base::raise( in, st... );
       }
 
+      template< typename ParseInput, typename State, typename... States >
+      static auto unwind( const ParseInput& in, State&& /*unused*/, States&&... st )
+         -> std::enable_if_t< internal::has_unwind< Base, void, const ParseInput&, States... > >
+      {
+         Base::unwind( in, st... );
+      }
+
       template< template< typename... > class Action, typename Iterator, typename ParseInput, typename State, typename... States >
       static auto apply( const Iterator& begin, const ParseInput& in, State&& /*unused*/, States&&... st ) noexcept( noexcept( Base::template apply< Action >( begin, in, st... ) ) )
          -> decltype( Base::template apply< Action >( begin, in, st... ) )
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp b/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
index 5fb778b7dbb4d45c29107e987697bc5393b75658..a9006fe1d13e25d4d7535b94f9e8a4b00c19dc71 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/remove_last_states.hpp
@@ -9,6 +9,8 @@
 
 #include "../config.hpp"
 
+#include "../internal/has_unwind.hpp"
+
 namespace TAO_PEGTL_NAMESPACE
 {
    // Remove the last N states of start(), success(), failure(), raise(), apply(), and apply0()
@@ -64,6 +66,20 @@ namespace TAO_PEGTL_NAMESPACE
          raise_impl( in, std::tie( st... ), std::make_index_sequence< sizeof...( st ) - N >() );
       }
 
+      template< typename ParseInput, typename Tuple, std::size_t... Is >
+      static auto unwind_impl( const ParseInput& in, const Tuple& t, std::index_sequence< Is... > /*unused*/ )
+         -> std::enable_if_t< internal::has_unwind< Base, void, const ParseInput&, std::tuple_element_t< Is, Tuple >... > >
+      {
+         Base::unwind( in, std::get< Is >( t )... );
+      }
+
+      template< typename ParseInput, typename... States >
+      static auto unwind( const ParseInput& in, States&&... st )
+         -> decltype( unwind_impl( in, std::tie( st... ), std::make_index_sequence< sizeof...( st ) - N >() ) )
+      {
+         unwind_impl( in, std::tie( st... ), std::make_index_sequence< sizeof...( st ) - N >() );
+      }
+
       template< template< typename... > class Action, typename Iterator, typename ParseInput, typename Tuple, std::size_t... Is >
       static auto apply_impl( const Iterator& begin, const ParseInput& in, const Tuple& t, std::index_sequence< Is... > /*unused*/ ) noexcept( noexcept( Base::template apply< Action >( begin, in, std::get< Is >( t )... ) ) )
          -> decltype( Base::template apply< Action >( begin, in, std::get< Is >( t )... ) )
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp b/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
index 7ec4639ef68c435f13d585b87a41fa8a5c7784e1..ee0a37820b2af3e548d61449383c246fd99cf2cf 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/rep_one_min_max.hpp
@@ -48,6 +48,28 @@ namespace TAO_PEGTL_NAMESPACE
          }
       };
 
+      template< unsigned Max, char C >
+      struct rep_one_min_max< 0, Max, C >
+      {
+         using rule_t = rep_one_min_max;
+         using subs_t = empty_list;
+
+         template< typename ParseInput >
+         [[nodiscard]] static bool match( ParseInput& in )
+         {
+            const auto size = in.size( Max + 1 );
+            std::size_t i = 0;
+            while( ( i < size ) && ( in.peek_char( i ) == C ) ) {
+               ++i;
+            }
+            if( i <= Max ) {
+               bump_help< result_on_found::success, ParseInput, char, C >( in, i );
+               return true;
+            }
+            return false;
+         }
+      };
+
       template< unsigned Min, unsigned Max, char C >
       inline constexpr bool enable_control< rep_one_min_max< Min, Max, C > > = false;
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp b/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
index e081051405ad231b944c5b2a14e5ddbfb25bef04..43912c4f26631b008cc9e4f1f029fcabfed9da6b 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/shuffle_states.hpp
@@ -5,10 +5,13 @@
 #define TAO_PEGTL_CONTRIB_SHUFFLE_STATES_HPP
 
 #include <tuple>
+#include <type_traits>
 #include <utility>
 
 #include "../config.hpp"
 
+#include "../internal/has_unwind.hpp"
+
 namespace TAO_PEGTL_NAMESPACE
 {
    namespace internal
@@ -112,6 +115,27 @@ namespace TAO_PEGTL_NAMESPACE
          Base::raise( in, st );
       }
 
+      template< typename ParseInput, typename Tuple, std::size_t... Is >
+      static auto unwind_impl( const ParseInput& in, const Tuple& t, std::index_sequence< Is... > /*unused*/ )
+         -> std::enable_if_t< internal::has_unwind< Base, void, const ParseInput&, std::tuple_element_t< Shuffle::template value< Is, sizeof...( Is ) >, Tuple >... > >
+      {
+         Base::unwind( in, std::get< Shuffle::template value< Is, sizeof...( Is ) > >( t )... );
+      }
+
+      template< typename ParseInput, typename... States >
+      static auto unwind( const ParseInput& in, States&&... st )
+         -> decltype( unwind_impl( in, std::tie( st... ), std::make_index_sequence< sizeof...( st ) >() ) )
+      {
+         unwind_impl( in, std::tie( st... ), std::make_index_sequence< sizeof...( st ) >() );
+      }
+
+      template< typename ParseInput, typename State >
+      static auto unwind( const ParseInput& in, State&& st )
+         -> std::enable_if_t< internal::has_unwind< Base, void, const ParseInput&, State > >
+      {
+         Base::unwind( in, st );
+      }
+
       template< template< typename... > class Action, typename Iterator, typename ParseInput, typename Tuple, std::size_t... Is >
       static auto apply_impl( const Iterator& begin, const ParseInput& in, const Tuple& t, std::index_sequence< Is... > /*unused*/ ) noexcept( noexcept( Base::template apply< Action >( begin, in, std::get< Shuffle::template value< Is, sizeof...( Is ) > >( t )... ) ) )
          -> decltype( Base::template apply< Action >( begin, in, std::get< Shuffle::template value< Is, sizeof...( Is ) > >( t )... ) )
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp b/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..28b105e913ae7a366d3cfa7c83bf3205e51a0b15
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/contrib/state_control.hpp
@@ -0,0 +1,118 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_CONTRIB_STATE_CONTROL_HPP
+#define TAO_PEGTL_CONTRIB_STATE_CONTROL_HPP
+
+#include <type_traits>
+
+#include "shuffle_states.hpp"
+
+#include "../config.hpp"
+#include "../internal/has_unwind.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   template< template< typename... > class Control >
+   struct state_control
+   {
+      template< typename Rule >
+      struct control
+         : Control< Rule >
+      {
+         static constexpr bool enable = true;
+
+         template< typename ParseInput, typename State, typename... States >
+         static void start( [[maybe_unused]] const ParseInput& in, [[maybe_unused]] State& state, [[maybe_unused]] States&&... st )
+         {
+            if constexpr( Control< Rule >::enable ) {
+               Control< Rule >::start( in, st... );
+            }
+            if constexpr( State::template enable< Rule > ) {
+               state.template start< Rule >( in, st... );
+            }
+#if defined( _MSC_VER )
+            ( (void)st, ... );
+#endif
+         }
+
+         template< typename ParseInput, typename State, typename... States >
+         static void success( [[maybe_unused]] const ParseInput& in, [[maybe_unused]] State& state, [[maybe_unused]] States&&... st )
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template success< Rule >( in, st... );
+            }
+            if constexpr( Control< Rule >::enable ) {
+               Control< Rule >::success( in, st... );
+            }
+#if defined( _MSC_VER )
+            ( (void)st, ... );
+#endif
+         }
+
+         template< typename ParseInput, typename State, typename... States >
+         static void failure( [[maybe_unused]] const ParseInput& in, [[maybe_unused]] State& state, [[maybe_unused]] States&&... st )
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template failure< Rule >( in, st... );
+            }
+            if constexpr( Control< Rule >::enable ) {
+               Control< Rule >::failure( in, st... );
+            }
+#if defined( _MSC_VER )
+            ( (void)st, ... );
+#endif
+         }
+
+         template< typename ParseInput, typename State, typename... States >
+         [[noreturn]] static void raise( const ParseInput& in, [[maybe_unused]] State& state, States&&... st )
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template raise< Rule >( in, st... );
+            }
+            Control< Rule >::raise( in, st... );
+         }
+
+         template< typename ParseInput, typename State, typename... States >
+         static auto unwind( [[maybe_unused]] const ParseInput& in, [[maybe_unused]] State& state, [[maybe_unused]] States&&... st )
+            -> std::enable_if_t< State::template enable< Rule > || ( Control< Rule >::enable && internal::has_unwind< Control< Rule >, void, const ParseInput&, States... > ) >
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template unwind< Rule >( in, st... );
+            }
+            if constexpr( Control< Rule >::enable && internal::has_unwind< Control< Rule >, void, const ParseInput&, States... > ) {
+               Control< Rule >::unwind( in, st... );
+            }
+#if defined( _MSC_VER )
+            ( (void)st, ... );
+#endif
+         }
+
+         template< template< typename... > class Action, typename Iterator, typename ParseInput, typename State, typename... States >
+         static auto apply( const Iterator& begin, const ParseInput& in, [[maybe_unused]] State& state, States&&... st )
+            -> decltype( Control< Rule >::template apply< Action >( begin, in, st... ) )
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template apply< Rule >( in, st... );
+            }
+            return Control< Rule >::template apply< Action >( begin, in, st... );
+         }
+
+         template< template< typename... > class Action, typename ParseInput, typename State, typename... States >
+         static auto apply0( const ParseInput& in, [[maybe_unused]] State& state, States&&... st )
+            -> decltype( Control< Rule >::template apply0< Action >( in, st... ) )
+         {
+            if constexpr( State::template enable< Rule > ) {
+               state.template apply0< Rule >( in, st... );
+            }
+            return Control< Rule >::template apply0< Action >( in, st... );
+         }
+      };
+
+      template< typename Rule >
+      using type = rotate_states_right< control< Rule > >;
+   };
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp b/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
index 798c951e6ab9cb518ab10622b2c4482ca326e537..9bc5976ac71a391c73bdc2657fca934e9bd9cbcc 100644
--- a/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
+++ b/packages/PEGTL/include/tao/pegtl/contrib/trace.hpp
@@ -1,153 +1,226 @@
-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #ifndef TAO_PEGTL_CONTRIB_TRACE_HPP
 #define TAO_PEGTL_CONTRIB_TRACE_HPP
 
-#include <cassert>
+#include <cstddef>
 #include <iomanip>
 #include <iostream>
-#include <utility>
-#include <vector>
+#include <string_view>
+#include <tuple>
 
+#include "state_control.hpp"
+
+#include "../apply_mode.hpp"
 #include "../config.hpp"
+#include "../demangle.hpp"
 #include "../normal.hpp"
-
-#include "../internal/demangle.hpp"
+#include "../nothing.hpp"
+#include "../parse.hpp"
+#include "../rewind_mode.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   namespace internal
+   template< bool HideInternal = false, bool UseColor = true, std::size_t IndentIncrement = 2, std::size_t InitialIndent = 8 >
+   struct tracer_traits
    {
+      template< typename Rule >
+      static constexpr bool enable = ( HideInternal ? normal< Rule >::enable : true );
+
+      static constexpr std::size_t initial_indent = InitialIndent;
+      static constexpr std::size_t indent_increment = IndentIncrement;
+
+      static constexpr std::string_view ansi_reset = UseColor ? "\033[m" : "";
+      static constexpr std::string_view ansi_rule = UseColor ? "\033[36m" : "";
+      static constexpr std::string_view ansi_hide = UseColor ? "\033[37m" : "";
+
+      static constexpr std::string_view ansi_position = UseColor ? "\033[1;34m" : "";
+      static constexpr std::string_view ansi_success = UseColor ? "\033[32m" : "";
+      static constexpr std::string_view ansi_failure = UseColor ? "\033[31m" : "";
+      static constexpr std::string_view ansi_raise = UseColor ? "\033[1;31m" : "";
+      static constexpr std::string_view ansi_unwind = UseColor ? "\033[31m" : "";
+      static constexpr std::string_view ansi_apply = UseColor ? "\033[1;36m" : "";
+   };
+
+   using standard_tracer_traits = tracer_traits< true >;
+   using complete_tracer_traits = tracer_traits< false >;
+
+   template< typename TracerTraits >
+   struct tracer
+   {
+      const std::ios_base::fmtflags m_flags;
+      std::size_t m_count = 0;
+      std::vector< std::size_t > m_stack;
+      position m_position;
+
+      template< typename Rule >
+      static constexpr bool enable = TracerTraits::template enable< Rule >;
+
       template< typename ParseInput >
-      void print_current( const ParseInput& in )
+      explicit tracer( const ParseInput& in )
+         : m_flags( std::cerr.flags() ),
+           m_position( in.position() )
       {
-         if( in.empty() ) {
-            std::cerr << "<eof>";
-         }
-         else {
-            const auto c = in.peek_uint8();
-            switch( c ) {
-               case 0:
-                  std::cerr << "<nul> = ";
-                  break;
-               case 9:
-                  std::cerr << "<ht> = ";
-                  break;
-               case 10:
-                  std::cerr << "<lf> = ";
-                  break;
-               case 13:
-                  std::cerr << "<cr> = ";
-                  break;
-               default:
-                  if( isprint( c ) ) {
-                     std::cerr << '\'' << c << "' = ";
-                  }
-            }
-            std::cerr << "(char)" << unsigned( c );
-         }
+         std::cerr << std::left;
+         print_position();
       }
 
-   }  // namespace internal
+      tracer( const tracer& ) = delete;
+      tracer( tracer&& ) = delete;
 
-   struct trace_state
-   {
-      unsigned rule = 0;
-      unsigned line = 0;
-      std::vector< unsigned > stack;
-   };
+      ~tracer()
+      {
+         std::cerr.flags( m_flags );
+      }
 
-   template< typename Rule, template< typename... > class Control >
-   struct basic_trace_control
-      : Control< Rule >
-   {
-      template< typename ParseInput, typename... States >
-      static void start( const ParseInput& in, States&&... st )
+      tracer& operator=( const tracer& ) = delete;
+      tracer& operator=( tracer&& ) = delete;
+
+      [[nodiscard]] std::size_t indent() const noexcept
+      {
+         return TracerTraits::initial_indent + TracerTraits::indent_increment * m_stack.size();
+      }
+
+      void print_position() const
       {
-         std::cerr << in.position() << "  start  " << internal::demangle< Rule >() << "; current ";
-         print_current( in );
-         std::cerr << std::endl;
-         Control< Rule >::start( in, st... );
+         std::cerr << std::setw( indent() ) << ' ' << TracerTraits::ansi_position << "position" << TracerTraits::ansi_reset << ' ' << m_position << '\n';
       }
 
-      template< typename ParseInput, typename... States >
-      static void start( const ParseInput& in, trace_state& ts, States&&... st )
+      void update_position( const position& p )
       {
-         std::cerr << std::setw( 6 ) << ++ts.line << " " << std::setw( 6 ) << ++ts.rule << " ";
-         start( in, st... );
-         ts.stack.push_back( ts.rule );
+         if( m_position != p ) {
+            m_position = p;
+            print_position();
+         }
       }
 
-      template< typename ParseInput, typename... States >
-      static void success( const ParseInput& in, States&&... st )
+      template< typename Rule, typename ParseInput, typename... States >
+      void start( const ParseInput& /*unused*/, States&&... /*unused*/ )
       {
-         std::cerr << in.position() << " success " << internal::demangle< Rule >() << "; next ";
-         print_current( in );
-         std::cerr << std::endl;
-         Control< Rule >::success( in, st... );
+         std::cerr << '#' << std::setw( indent() - 1 ) << ++m_count << TracerTraits::ansi_rule << demangle< Rule >() << TracerTraits::ansi_reset << '\n';
+         m_stack.push_back( m_count );
       }
 
-      template< typename ParseInput, typename... States >
-      static void success( const ParseInput& in, trace_state& ts, States&&... st )
+      template< typename Rule, typename ParseInput, typename... States >
+      void success( const ParseInput& in, States&&... /*unused*/ )
       {
-         assert( !ts.stack.empty() );
-         std::cerr << std::setw( 6 ) << ++ts.line << " " << std::setw( 6 ) << ts.stack.back() << " ";
-         success( in, st... );
-         ts.stack.pop_back();
+         const auto prev = m_stack.back();
+         m_stack.pop_back();
+         std::cerr << std::setw( indent() ) << ' ' << TracerTraits::ansi_success << "success" << TracerTraits::ansi_reset;
+         if( m_count != prev ) {
+            std::cerr << " #" << prev << ' ' << TracerTraits::ansi_hide << demangle< Rule >() << TracerTraits::ansi_reset;
+         }
+         std::cerr << '\n';
+         update_position( in.position() );
       }
 
-      template< typename ParseInput, typename... States >
-      static void failure( const ParseInput& in, States&&... st )
+      template< typename Rule, typename ParseInput, typename... States >
+      void failure( const ParseInput& in, States&&... /*unused*/ )
       {
-         std::cerr << in.position() << " failure " << internal::demangle< Rule >() << std::endl;
-         Control< Rule >::failure( in, st... );
+         const auto prev = m_stack.back();
+         m_stack.pop_back();
+         std::cerr << std::setw( indent() ) << ' ' << TracerTraits::ansi_failure << "failure" << TracerTraits::ansi_reset;
+         if( m_count != prev ) {
+            std::cerr << " #" << prev << ' ' << TracerTraits::ansi_hide << demangle< Rule >() << TracerTraits::ansi_reset;
+         }
+         std::cerr << '\n';
+         update_position( in.position() );
       }
 
-      template< typename ParseInput, typename... States >
-      static void failure( const ParseInput& in, trace_state& ts, States&&... st )
+      template< typename Rule, typename ParseInput, typename... States >
+      void raise( const ParseInput& /*unused*/, States&&... /*unused*/ )
       {
-         assert( !ts.stack.empty() );
-         std::cerr << std::setw( 6 ) << ++ts.line << " " << std::setw( 6 ) << ts.stack.back() << " ";
-         failure( in, st... );
-         ts.stack.pop_back();
+         std::cerr << std::setw( indent() ) << ' ' << TracerTraits::ansi_raise << "raise" << TracerTraits::ansi_reset << ' ' << TracerTraits::ansi_rule << demangle< Rule >() << TracerTraits::ansi_reset << '\n';
+      }
+
+      template< typename Rule, typename ParseInput, typename... States >
+      void unwind( const ParseInput& in, States&&... /*unused*/ )
+      {
+         const auto prev = m_stack.back();
+         m_stack.pop_back();
+         std::cerr << std::setw( indent() ) << ' ' << TracerTraits::ansi_unwind << "unwind" << TracerTraits::ansi_reset;
+         if( m_count != prev ) {
+            std::cerr << " #" << prev << ' ' << TracerTraits::ansi_hide << demangle< Rule >() << TracerTraits::ansi_reset;
+         }
+         std::cerr << '\n';
+         update_position( in.position() );
       }
 
-      template< template< typename... > class Action, typename Iterator, typename ParseInput, typename... States >
-      static auto apply( const Iterator& begin, const ParseInput& in, States&&... st )
-         -> decltype( Control< Rule >::template apply< Action >( begin, in, st... ) )
+      template< typename Rule, typename ParseInput, typename... States >
+      void apply( const ParseInput& /*unused*/, States&&... /*unused*/ )
       {
-         std::cerr << in.position() << "  apply  " << internal::demangle< Rule >() << std::endl;
-         return Control< Rule >::template apply< Action >( begin, in, st... );
+         std::cerr << std::setw( static_cast< int >( indent() - TracerTraits::indent_increment ) ) << ' ' << TracerTraits::ansi_apply << "apply" << TracerTraits::ansi_reset << '\n';
       }
 
-      template< template< typename... > class Action, typename Iterator, typename ParseInput, typename... States >
-      static auto apply( const Iterator& begin, const ParseInput& in, trace_state& ts, States&&... st )
-         -> decltype( apply< Action >( begin, in, st... ) )
+      template< typename Rule, typename ParseInput, typename... States >
+      void apply0( const ParseInput& /*unused*/, States&&... /*unused*/ )
       {
-         std::cerr << std::setw( 6 ) << ++ts.line << "        ";
-         return apply< Action >( begin, in, st... );
+         std::cerr << std::setw( static_cast< int >( indent() - TracerTraits::indent_increment ) ) << ' ' << TracerTraits::ansi_apply << "apply0" << TracerTraits::ansi_reset << '\n';
       }
 
-      template< template< typename... > class Action, typename ParseInput, typename... States >
-      static auto apply0( const ParseInput& in, States&&... st )
-         -> decltype( Control< Rule >::template apply0< Action >( in, st... ) )
+      template< typename Rule,
+                template< typename... > class Action = nothing,
+                template< typename... > class Control = normal,
+                typename ParseInput,
+                typename... States >
+      bool parse( ParseInput&& in, States&&... st )
       {
-         std::cerr << in.position() << "  apply0 " << internal::demangle< Rule >() << std::endl;
-         return Control< Rule >::template apply0< Action >( in, st... );
+         return TAO_PEGTL_NAMESPACE::parse< Rule, Action, state_control< Control >::template type >( in, st..., *this );
       }
+   };
 
-      template< template< typename... > class Action, typename ParseInput, typename... States >
-      static auto apply0( const ParseInput& in, trace_state& ts, States&&... st )
-         -> decltype( apply0< Action >( in, st... ) )
+   template< typename Rule,
+             template< typename... > class Action = nothing,
+             template< typename... > class Control = normal,
+             typename ParseInput,
+             typename... States >
+   bool standard_trace( ParseInput&& in, States&&... st )
+   {
+      tracer< standard_tracer_traits > tr( in );
+      return tr.parse< Rule, Action, Control >( in, st... );
+   }
+
+   template< typename Rule,
+             template< typename... > class Action = nothing,
+             template< typename... > class Control = normal,
+             typename ParseInput,
+             typename... States >
+   bool complete_trace( ParseInput&& in, States&&... st )
+   {
+      tracer< complete_tracer_traits > tr( in );
+      return tr.parse< Rule, Action, Control >( in, st... );
+   }
+
+   template< typename Tracer >
+   struct trace
+      : maybe_nothing
+   {
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
       {
-         std::cerr << std::setw( 6 ) << ++ts.line << "        ";
-         return apply0< Action >( in, st... );
+         if constexpr( sizeof...( st ) == 0 ) {
+            return TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, state_control< Control >::template type >( in, st..., Tracer( in ) );
+         }
+         else if constexpr( !std::is_same_v< std::tuple_element_t< sizeof...( st ) - 1, std::tuple< States... > >, Tracer& > ) {
+            return TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, state_control< Control >::template type >( in, st..., Tracer( in ) );
+         }
+         else {
+            return TAO_PEGTL_NAMESPACE::match< Rule, A, M, Action, Control >( in, st... );
+         }
       }
    };
 
-   template< typename Rule >
-   using trace_control = basic_trace_control< Rule, normal >;
+   using trace_standard = trace< tracer< standard_tracer_traits > >;
+   using trace_complete = trace< tracer< complete_tracer_traits > >;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/contrib/visit_rt.hpp b/packages/PEGTL/include/tao/pegtl/contrib/visit_rt.hpp
deleted file mode 100644
index b4958e84399f2af15b0da7b940a2472fe52b16e2..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/contrib/visit_rt.hpp
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_CONTRIB_VISIT_RT_HPP
-#define TAO_PEGTL_CONTRIB_VISIT_RT_HPP
-
-#include <set>
-#include <string_view>
-
-#include "../config.hpp"
-#include "../type_list.hpp"
-
-#include "../internal/demangle.hpp"
-
-namespace TAO_PEGTL_NAMESPACE
-{
-   namespace internal
-   {
-      template< template< typename... > class Func, typename... Rules >
-      struct visitor_rt
-      {
-         template< typename... Args >
-         static void visit( std::set< std::string_view >& done, Args&&... args )
-         {
-            ( visit_rule< Rules >( typename Rules::subs_t(), done, args... ), ... );
-         }
-
-      private:
-         template< typename Rule, typename... Subs, typename... Args >
-         static void visit_rule( type_list< Subs... > /*unused*/, std::set< std::string_view >& done, Args&&... args )
-         {
-            if( done.emplace( demangle< Rule >() ).second ) {
-               Func< Rule >::visit( args... );
-               visitor_rt< Func, Subs... >::visit( done, args... );
-            }
-         }
-      };
-
-   }  // namespace internal
-
-   template< typename Rule, template< typename... > class Func, typename... Args >
-   std::size_t visit_rt( Args&&... args )
-   {
-      std::set< std::string_view > done;
-      internal::visitor_rt< Func, Rule >::visit( done, args... );
-      return done.size();
-   }
-
-}  // namespace TAO_PEGTL_NAMESPACE
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/cstream_input.hpp b/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
index cebc6f7aa6201b877991cd73bb0b5804092848b8..ad50942faf93da27efaebb8a15bf6b050ea51169 100644
--- a/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/cstream_input.hpp
@@ -25,7 +25,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   cstream_input( Ts&&... )->cstream_input<>;
+   cstream_input( Ts&&... ) -> cstream_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/demangle.hpp b/packages/PEGTL/include/tao/pegtl/demangle.hpp
similarity index 94%
rename from packages/PEGTL/include/tao/pegtl/internal/demangle.hpp
rename to packages/PEGTL/include/tao/pegtl/demangle.hpp
index 183134309bda8bd43843fb10f3db68880fc5788e..4122ce4afcee169ae4fbcb549704bc135dc81425 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/demangle.hpp
+++ b/packages/PEGTL/include/tao/pegtl/demangle.hpp
@@ -1,15 +1,13 @@
 // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL
 
-#ifndef TAO_PEGTL_INTERNAL_DEMANGLE_HPP
-#define TAO_PEGTL_INTERNAL_DEMANGLE_HPP
+#ifndef TAO_PEGTL_DEMANGLE_HPP
+#define TAO_PEGTL_DEMANGLE_HPP
 
 #include <ciso646>
 #include <string_view>
 
-#include "../config.hpp"
-
-namespace TAO_PEGTL_NAMESPACE::internal
+namespace tao
 {
 #if defined( __clang__ )
 
@@ -135,6 +133,6 @@ namespace TAO_PEGTL_NAMESPACE::internal
 
 #endif
 
-}  // namespace TAO_PEGTL_NAMESPACE::internal
+}  // namespace tao
 
 #endif
diff --git a/packages/PEGTL/include/tao/pegtl/file_input.hpp b/packages/PEGTL/include/tao/pegtl/file_input.hpp
index d54db990fb4bbba59e483a5e5831ff09f9289881..1fba74bb583d95d15deea4076919de820c00e559 100644
--- a/packages/PEGTL/include/tao/pegtl/file_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/file_input.hpp
@@ -37,7 +37,7 @@ namespace TAO_PEGTL_NAMESPACE
 #endif
 
    template< typename... Ts >
-   explicit file_input( Ts&&... )->file_input<>;
+   explicit file_input( Ts&&... ) -> file_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/alnum.hpp b/packages/PEGTL/include/tao/pegtl/internal/alnum.hpp
deleted file mode 100644
index 45704b90475acfcc19196b1452416d5d843f2a2f..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/internal/alnum.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_INTERNAL_ALNUM_HPP
-#define TAO_PEGTL_INTERNAL_ALNUM_HPP
-
-#include "../config.hpp"
-
-#include "peek_char.hpp"
-#include "ranges.hpp"
-
-namespace TAO_PEGTL_NAMESPACE::internal
-{
-   using alnum = ranges< peek_char, 'a', 'z', 'A', 'Z', '0', '9' >;
-
-}  // namespace TAO_PEGTL_NAMESPACE::internal
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/alpha.hpp b/packages/PEGTL/include/tao/pegtl/internal/alpha.hpp
deleted file mode 100644
index b1591d92b58c847655ee292149b47e135dd54fb4..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/internal/alpha.hpp
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_INTERNAL_ALPHA_HPP
-#define TAO_PEGTL_INTERNAL_ALPHA_HPP
-
-#include "../config.hpp"
-
-#include "peek_char.hpp"
-#include "ranges.hpp"
-
-namespace TAO_PEGTL_NAMESPACE::internal
-{
-   using alpha = ranges< peek_char, 'a', 'z', 'A', 'Z' >;
-
-}  // namespace TAO_PEGTL_NAMESPACE::internal
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
index 92e24ef26b528eb3a8370ff88ff577b45529e461..7dac482bffd3cbf166e0f9c5dc48714fb89bf201 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply.hpp
@@ -29,7 +29,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
                 class Control,
                 typename ParseInput,
                 typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
+      [[nodiscard]] static bool match( [[maybe_unused]] ParseInput& in, [[maybe_unused]] States&&... st )
       {
          if constexpr( ( A == apply_mode::action ) && ( sizeof...( Actions ) > 0 ) ) {
             using action_t = typename ParseInput::action_t;
@@ -38,8 +38,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
          }
          else {
 #if defined( _MSC_VER )
-            (void)in;
-            (void)( (void)st, ... );
+            ( (void)st, ... );
 #endif
             return true;
          }
diff --git a/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp b/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
index 3ad4b3c2a2800ca4d5e5cc047dc7fcfb469ae417..7c3b92c626b83d630856fd07ae7aaeffdf100e9c 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/apply0.hpp
@@ -29,14 +29,14 @@ namespace TAO_PEGTL_NAMESPACE::internal
                 class Control,
                 typename ParseInput,
                 typename... States >
-      [[nodiscard]] static bool match( ParseInput& /*unused*/, States&&... st )
+      [[nodiscard]] static bool match( ParseInput& /*unused*/, [[maybe_unused]] States&&... st )
       {
          if constexpr( A == apply_mode::action ) {
             return ( apply0_single< Actions >::match( st... ) && ... );
          }
          else {
 #if defined( _MSC_VER )
-            (void)( (void)st, ... );
+            ( (void)st, ... );
 #endif
             return true;
          }
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bol.hpp b/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
index 3304cc5d4ff47005570e420c66ed86cc1bb3ce52..20f029e54204ad9ae0f21946de63a357d90e1b81 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bol.hpp
@@ -5,11 +5,10 @@
 #define TAO_PEGTL_INTERNAL_BOL_HPP
 
 #include "../config.hpp"
+#include "../type_list.hpp"
 
 #include "enable_control.hpp"
 
-#include "../type_list.hpp"
-
 namespace TAO_PEGTL_NAMESPACE::internal
 {
    struct bol
@@ -20,7 +19,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
       template< typename ParseInput >
       [[nodiscard]] static bool match( ParseInput& in ) noexcept
       {
-         return in.byte_in_line() == 0;
+         return in.column() == 1;
       }
    };
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/bump.hpp b/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
index 74a1a7d7c749d12c55d570d762d590a5aa8aa554..fded8cc2e2315b25c5dbf170bddfe5729fdf3285 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/bump.hpp
@@ -15,10 +15,10 @@ namespace TAO_PEGTL_NAMESPACE::internal
       for( std::size_t i = 0; i < count; ++i ) {
          if( iter.data[ i ] == ch ) {
             ++iter.line;
-            iter.byte_in_line = 0;
+            iter.column = 1;
          }
          else {
-            ++iter.byte_in_line;
+            ++iter.column;
          }
       }
       iter.byte += count;
@@ -29,14 +29,14 @@ namespace TAO_PEGTL_NAMESPACE::internal
    {
       iter.data += count;
       iter.byte += count;
-      iter.byte_in_line += count;
+      iter.column += count;
    }
 
    inline void bump_to_next_line( iterator& iter, const std::size_t count ) noexcept
    {
       ++iter.line;
       iter.byte += count;
-      iter.byte_in_line = 0;
+      iter.column = 1;
       iter.data += count;
    }
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/dusel_mode.hpp b/packages/PEGTL/include/tao/pegtl/internal/dusel_mode.hpp
deleted file mode 100644
index dadba26303728559424379934ab53eef38d4a3c1..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/internal/dusel_mode.hpp
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2017-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_INTERNAL_DUSEL_MODE_HPP
-#define TAO_PEGTL_INTERNAL_DUSEL_MODE_HPP
-
-#include "../config.hpp"
-
-namespace TAO_PEGTL_NAMESPACE::internal
-{
-   enum class dusel_mode : char
-   {
-      nothing = 0,
-      control = 1,
-      control_and_apply_void = 2,
-      control_and_apply_bool = 3,
-      control_and_apply0_void = 4,
-      control_and_apply0_bool = 5
-   };
-
-}  // namespace TAO_PEGTL_NAMESPACE::internal
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/duseltronik.hpp b/packages/PEGTL/include/tao/pegtl/internal/duseltronik.hpp
deleted file mode 100644
index 53555e9faf04b63311c39eb15234fb35a9518db2..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/internal/duseltronik.hpp
+++ /dev/null
@@ -1,187 +0,0 @@
-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_INTERNAL_DUSELTRONIK_HPP
-#define TAO_PEGTL_INTERNAL_DUSELTRONIK_HPP
-
-#include "../apply_mode.hpp"
-#include "../config.hpp"
-#include "../rewind_mode.hpp"
-
-#include "dusel_mode.hpp"
-
-#ifdef _MSC_VER
-#pragma warning( push )
-#pragma warning( disable : 4702 )
-#endif
-
-namespace TAO_PEGTL_NAMESPACE::internal
-{
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control,
-             dusel_mode = dusel_mode::nothing >
-   struct duseltronik;
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::nothing >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static auto match( ParseInput& in, States&&... st )
-         -> decltype( Rule::template match< A, M, Action, Control >( in, st... ) )
-      {
-         return Rule::template match< A, M, Action, Control >( in, st... );
-      }
-
-      template< typename ParseInput, typename... States, int = 1 >
-      [[nodiscard]] static auto match( ParseInput& in, States&&... /*unused*/ )
-         -> decltype( Rule::match( in ) )
-      {
-         return Rule::match( in );
-      }
-   };
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::control >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-      {
-         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
-
-         if( duseltronik< Rule, A, M, Action, Control, dusel_mode::nothing >::match( in, st... ) ) {
-            Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
-            return true;
-         }
-         Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
-         return false;
-      }
-   };
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::control_and_apply_void >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-      {
-         auto m = in.template mark< rewind_mode::required >();
-
-         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
-
-         if( duseltronik< Rule, A, rewind_mode::active, Action, Control, dusel_mode::nothing >::match( in, st... ) ) {
-            Control< Rule >::template apply< Action >( m.iterator(), static_cast< const ParseInput& >( in ), st... );
-            Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
-            return m( true );
-         }
-         Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
-         return false;
-      }
-   };
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::control_and_apply_bool >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-      {
-         auto m = in.template mark< rewind_mode::required >();
-
-         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
-
-         if( duseltronik< Rule, A, rewind_mode::active, Action, Control, dusel_mode::nothing >::match( in, st... ) ) {
-            if( Control< Rule >::template apply< Action >( m.iterator(), static_cast< const ParseInput& >( in ), st... ) ) {
-               Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
-               return m( true );
-            }
-         }
-         Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
-         return false;
-      }
-   };
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::control_and_apply0_void >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-      {
-         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
-
-         if( duseltronik< Rule, A, M, Action, Control, dusel_mode::nothing >::match( in, st... ) ) {
-            Control< Rule >::template apply0< Action >( static_cast< const ParseInput& >( in ), st... );
-            Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
-            return true;
-         }
-         Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
-         return false;
-      }
-   };
-
-   template< typename Rule,
-             apply_mode A,
-             rewind_mode M,
-             template< typename... >
-             class Action,
-             template< typename... >
-             class Control >
-   struct duseltronik< Rule, A, M, Action, Control, dusel_mode::control_and_apply0_bool >
-   {
-      template< typename ParseInput, typename... States >
-      [[nodiscard]] static bool match( ParseInput& in, States&&... st )
-      {
-         auto m = in.template mark< rewind_mode::required >();
-
-         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
-
-         if( duseltronik< Rule, A, rewind_mode::active, Action, Control, dusel_mode::nothing >::match( in, st... ) ) {
-            if( Control< Rule >::template apply0< Action >( static_cast< const ParseInput& >( in ), st... ) ) {
-               Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
-               return m( true );
-            }
-         }
-         Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
-         return false;
-      }
-   };
-
-}  // namespace TAO_PEGTL_NAMESPACE::internal
-
-#ifdef _MSC_VER
-#pragma warning( pop )
-#endif
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
index cd4b5663e1c14c479835f1d2f7bfa88d48844bb5..86791340031f4f89b8aca1b90c5e807ec21e04eb 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_posix.hpp
@@ -4,22 +4,74 @@
 #ifndef TAO_PEGTL_INTERNAL_FILE_MAPPER_POSIX_HPP
 #define TAO_PEGTL_INTERNAL_FILE_MAPPER_POSIX_HPP
 
+#include <fcntl.h>
 #include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
-#include <system_error>
+#include <filesystem>
+#include <utility>
 
 #include "../config.hpp"
 
-#include "file_opener.hpp"
-
 namespace TAO_PEGTL_NAMESPACE::internal
 {
+   struct file_opener
+   {
+      explicit file_opener( const std::filesystem::path& path )  // NOLINT(modernize-pass-by-value)
+         : m_path( path ),
+           m_fd( open() )
+      {}
+
+      file_opener( const file_opener& ) = delete;
+      file_opener( file_opener&& ) = delete;
+
+      ~file_opener()
+      {
+         ::close( m_fd );
+      }
+
+      void operator=( const file_opener& ) = delete;
+      void operator=( file_opener&& ) = delete;
+
+      [[nodiscard]] std::size_t size() const
+      {
+         struct stat st;
+         errno = 0;
+         if( ::fstat( m_fd, &st ) < 0 ) {
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "fstat() failed", m_path, ec );
+         }
+         return std::size_t( st.st_size );
+      }
+
+      const std::filesystem::path m_path;
+      const int m_fd;
+
+   private:
+      [[nodiscard]] int open() const
+      {
+         errno = 0;
+         const int fd = ::open( m_path.c_str(),
+                                O_RDONLY
+#if defined( O_CLOEXEC )
+                                   | O_CLOEXEC
+#endif
+         );
+         if( fd >= 0 ) {
+            return fd;
+         }
+         const std::error_code ec( errno, std::system_category() );
+         throw std::filesystem::filesystem_error( "open() failed", m_path, ec );
+      }
+   };
+
    class file_mapper
    {
    public:
-      explicit file_mapper( const char* filename )
-         : file_mapper( file_opener( filename ) )
+      explicit file_mapper( const std::filesystem::path& path )
+         : file_mapper( file_opener( path ) )
       {}
 
       explicit file_mapper( const file_opener& reader )
@@ -27,15 +79,15 @@ namespace TAO_PEGTL_NAMESPACE::internal
            m_data( static_cast< const char* >( ::mmap( nullptr, m_size, PROT_READ, MAP_PRIVATE, reader.m_fd, 0 ) ) )
       {
          if( ( m_size != 0 ) && ( intptr_t( m_data ) == -1 ) ) {
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), reader.m_source );
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "mmap() failed", reader.m_path, ec );
          }
       }
 
       file_mapper( const file_mapper& ) = delete;
       file_mapper( file_mapper&& ) = delete;
 
-      ~file_mapper() noexcept
+      ~file_mapper()
       {
          // Legacy C interface requires pointer-to-mutable but does not write through the pointer.
          ::munmap( const_cast< char* >( m_data ), m_size );
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
index 27f9eaf955f141de0c02478d93d02a518af1bade..e8dab183317686ab86d49a511493892e20c90018 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_mapper_win32.hpp
@@ -26,7 +26,7 @@
 #undef TAO_PEGTL_WIN32_LEAN_AND_MEAN_WAS_DEFINED
 #endif
 
-#include <system_error>
+#include <filesystem>
 
 #include "../config.hpp"
 
@@ -34,15 +34,15 @@ namespace TAO_PEGTL_NAMESPACE::internal
 {
    struct win32_file_opener
    {
-      explicit win32_file_opener( const char* filename )
-         : m_source( filename ),
+      explicit win32_file_opener( const std::filesystem::path& path )
+         : m_path( path ),
            m_handle( open() )
       {}
 
       win32_file_opener( const win32_file_opener& ) = delete;
       win32_file_opener( win32_file_opener&& ) = delete;
 
-      ~win32_file_opener() noexcept
+      ~win32_file_opener()
       {
          ::CloseHandle( m_handle );
       }
@@ -54,23 +54,21 @@ namespace TAO_PEGTL_NAMESPACE::internal
       {
          LARGE_INTEGER size;
          if( !::GetFileSizeEx( m_handle, &size ) ) {
-            const auto ec = ::GetLastError();
-            throw std::system_error( ec, std::system_category(), std::string( "GetFileSizeEx(): " ) + m_source );
+            const std::error_code ec( ::GetLastError(), std::system_category() );
+            throw std::filesystem::filesystem_error( "GetFileSizeEx() failed", m_path, ec );
          }
          return std::size_t( size.QuadPart );
       }
 
-      const char* const m_source;
+      const std::filesystem::path m_path;
       const HANDLE m_handle;
 
    private:
       [[nodiscard]] HANDLE open() const
       {
          SetLastError( 0 );
-         std::wstring ws( m_source, m_source + strlen( m_source ) );
-
 #if( _WIN32_WINNT >= 0x0602 )
-         const HANDLE handle = ::CreateFile2( ws.c_str(),
+         const HANDLE handle = ::CreateFile2( m_path.c_str(),
                                               GENERIC_READ,
                                               FILE_SHARE_READ,
                                               OPEN_EXISTING,
@@ -78,10 +76,10 @@ namespace TAO_PEGTL_NAMESPACE::internal
          if( handle != INVALID_HANDLE_VALUE ) {
             return handle;
          }
-         const auto ec = ::GetLastError();
-         throw std::system_error( ec, std::system_category(), std::string( "CreateFile2(): " ) + m_source );
+         const std::error_code ec( ::GetLastError(), std::system_category() );
+         throw std::filesystem::filesystem_error( "CreateFile2() failed", m_path, ec );
 #else
-         const HANDLE handle = ::CreateFileW( ws.c_str(),
+         const HANDLE handle = ::CreateFileW( m_path.c_str(),
                                               GENERIC_READ,
                                               FILE_SHARE_READ,
                                               nullptr,
@@ -91,16 +89,16 @@ namespace TAO_PEGTL_NAMESPACE::internal
          if( handle != INVALID_HANDLE_VALUE ) {
             return handle;
          }
-         const auto ec = ::GetLastError();
-         throw std::system_error( ec, std::system_category(), std::string( "CreateFileW(): " ) + m_source );
+         const std::error_code ec( ::GetLastError(), std::system_category() );
+         throw std::filesystem::filesystem_error( "CreateFileW()", m_path, ec );
 #endif
       }
    };
 
    struct win32_file_mapper
    {
-      explicit win32_file_mapper( const char* filename )
-         : win32_file_mapper( win32_file_opener( filename ) )
+      explicit win32_file_mapper( const std::filesystem::path& path )
+         : win32_file_mapper( win32_file_opener( path ) )
       {}
 
       explicit win32_file_mapper( const win32_file_opener& reader )
@@ -111,7 +109,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
       win32_file_mapper( const win32_file_mapper& ) = delete;
       win32_file_mapper( win32_file_mapper&& ) = delete;
 
-      ~win32_file_mapper() noexcept
+      ~win32_file_mapper()
       {
          ::CloseHandle( m_handle );
       }
@@ -140,16 +138,16 @@ namespace TAO_PEGTL_NAMESPACE::internal
          if( handle != NULL || file_size == 0 ) {
             return handle;
          }
-         const auto ec = ::GetLastError();
-         throw std::system_error( ec, std::system_category(), std::string( "CreateFileMappingW(): " ) + reader.m_source );
+         const std::error_code ec( ::GetLastError(), std::system_category() );
+         throw std::filesystem::filesystem_error( "CreateFileMappingW() failed", reader.m_path, ec );
       }
    };
 
    class file_mapper
    {
    public:
-      explicit file_mapper( const char* filename )
-         : file_mapper( win32_file_mapper( filename ) )
+      explicit file_mapper( const std::filesystem::path& path )
+         : file_mapper( win32_file_mapper( path ) )
       {}
 
       explicit file_mapper( const win32_file_mapper& mapper )
@@ -161,15 +159,15 @@ namespace TAO_PEGTL_NAMESPACE::internal
                                                                 0 ) ) )
       {
          if( ( m_size != 0 ) && ( intptr_t( m_data ) == 0 ) ) {
-            const auto ec = ::GetLastError();
-            throw std::system_error( ec, std::system_category(), "MapViewOfFile()" );
+            const std::error_code ec( ::GetLastError(), std::system_category() );
+            throw std::filesystem::filesystem_error( "MapViewOfFile() failed", ec );
          }
       }
 
       file_mapper( const file_mapper& ) = delete;
       file_mapper( file_mapper&& ) = delete;
 
-      ~file_mapper() noexcept
+      ~file_mapper()
       {
          ::UnmapViewOfFile( LPCVOID( m_data ) );
       }
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_opener.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_opener.hpp
deleted file mode 100644
index 5f2521b6b1e3eafc4a7b294e8ce65f0dc2426c21..0000000000000000000000000000000000000000
--- a/packages/PEGTL/include/tao/pegtl/internal/file_opener.hpp
+++ /dev/null
@@ -1,71 +0,0 @@
-// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
-// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
-
-#ifndef TAO_PEGTL_INTERNAL_FILE_OPENER_HPP
-#define TAO_PEGTL_INTERNAL_FILE_OPENER_HPP
-
-#include <fcntl.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <system_error>
-#include <utility>
-
-#include "../config.hpp"
-
-namespace TAO_PEGTL_NAMESPACE::internal
-{
-   struct file_opener
-   {
-      explicit file_opener( const char* filename )
-         : m_source( filename ),
-           m_fd( open() )
-      {}
-
-      file_opener( const file_opener& ) = delete;
-      file_opener( file_opener&& ) = delete;
-
-      ~file_opener() noexcept
-      {
-         ::close( m_fd );
-      }
-
-      void operator=( const file_opener& ) = delete;
-      void operator=( file_opener&& ) = delete;
-
-      [[nodiscard]] std::size_t size() const
-      {
-         struct stat st;
-         errno = 0;
-         if( ::fstat( m_fd, &st ) < 0 ) {
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), m_source );
-         }
-         return std::size_t( st.st_size );
-      }
-
-      const char* const m_source;
-      const int m_fd;
-
-   private:
-      [[nodiscard]] int open() const
-      {
-         errno = 0;
-         const int fd = ::open( m_source,
-                                O_RDONLY
-#if defined( O_CLOEXEC )
-                                   | O_CLOEXEC
-#endif
-         );
-         if( fd >= 0 ) {
-            return fd;
-         }
-         const auto ec = errno;
-         throw std::system_error( ec, std::system_category(), m_source );
-      }
-   };
-
-}  // namespace TAO_PEGTL_NAMESPACE::internal
-
-#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp b/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
index ac098e5921edae0d14f0ed7efe7e298670b95c9a..d2812abfc8fc62726ee55c93679beb91e07aa4ed 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/file_reader.hpp
@@ -5,31 +5,37 @@
 #define TAO_PEGTL_INTERNAL_FILE_READER_HPP
 
 #include <cstdio>
+#include <filesystem>
 #include <memory>
 #include <string>
-#include <system_error>
 #include <utility>
 
 #include "../config.hpp"
 
 namespace TAO_PEGTL_NAMESPACE::internal
 {
-   [[nodiscard]] inline std::FILE* file_open( const char* filename )
+   [[nodiscard]] inline std::FILE* file_open( const std::filesystem::path& path )
    {
       errno = 0;
 #if defined( _MSC_VER )
       std::FILE* file;
-      if( ::fopen_s( &file, filename, "rb" ) == 0 )
-#elif defined( __MINGW32__ )
-      if( auto* file = std::fopen( filename, "rb" ) )
+      if( ::_wfopen_s( &file, path.c_str(), L"rb" ) == 0 ) {
+         return file;
+      }
+      const std::error_code ec( errno, std::system_category() );
+      throw std::filesystem::filesystem_error( "_wfopen_s() failed", path, ec );
 #else
-      if( auto* file = std::fopen( filename, "rbe" ) )
+#if defined( __MINGW32__ )
+      if( auto* file = std::fopen( path.c_str(), "rb" ) )
+#else
+      if( auto* file = std::fopen( path.c_str(), "rbe" ) )
 #endif
       {
          return file;
       }
-      const auto ec = errno;
-      throw std::system_error( ec, std::system_category(), filename );
+      const std::error_code ec( errno, std::system_category() );
+      throw std::filesystem::filesystem_error( "std::fopen() failed", path, ec );
+#endif
    }
 
    struct file_close
@@ -43,13 +49,12 @@ namespace TAO_PEGTL_NAMESPACE::internal
    class file_reader
    {
    public:
-      explicit file_reader( const char* filename )
-         : m_source( filename ),
-           m_file( file_open( m_source ) )
+      explicit file_reader( const std::filesystem::path& path )
+         : file_reader( file_open( path ), path )
       {}
 
-      file_reader( FILE* file, const char* filename ) noexcept
-         : m_source( filename ),
+      file_reader( FILE* file, const std::filesystem::path& path )  // NOLINT(modernize-pass-by-value)
+         : m_path( path ),
            m_file( file )
       {}
 
@@ -66,23 +71,23 @@ namespace TAO_PEGTL_NAMESPACE::internal
          errno = 0;
          if( std::fseek( m_file.get(), 0, SEEK_END ) != 0 ) {
             // LCOV_EXCL_START
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), m_source );
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "std::fseek() failed [SEEK_END]", m_path, ec );
             // LCOV_EXCL_STOP
          }
          errno = 0;
          const auto s = std::ftell( m_file.get() );
          if( s < 0 ) {
             // LCOV_EXCL_START
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), m_source );
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "std::ftell() failed", m_path, ec );
             // LCOV_EXCL_STOP
          }
          errno = 0;
          if( std::fseek( m_file.get(), 0, SEEK_SET ) != 0 ) {
             // LCOV_EXCL_START
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), m_source );
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "std::fseek() failed [SEEK_SET]", m_path, ec );
             // LCOV_EXCL_STOP
          }
          return std::size_t( s );
@@ -95,15 +100,15 @@ namespace TAO_PEGTL_NAMESPACE::internal
          errno = 0;
          if( !nrv.empty() && ( std::fread( &nrv[ 0 ], nrv.size(), 1, m_file.get() ) != 1 ) ) {
             // LCOV_EXCL_START
-            const auto ec = errno;
-            throw std::system_error( ec, std::system_category(), m_source );
+            const std::error_code ec( errno, std::system_category() );
+            throw std::filesystem::filesystem_error( "std::fread() failed", m_path, ec );
             // LCOV_EXCL_STOP
          }
          return nrv;
       }
 
    private:
-      const char* const m_source;
+      const std::filesystem::path m_path;
       const std::unique_ptr< std::FILE, file_close > m_file;
    };
 
diff --git a/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp b/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..20bd953f5b35b75f78588449507ae2234e0c899d
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/internal/has_unwind.hpp
@@ -0,0 +1,21 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_INTERNAL_HAS_UNWIND_HPP
+#define TAO_PEGTL_INTERNAL_HAS_UNWIND_HPP
+
+#include <utility>
+
+#include "../config.hpp"
+
+namespace TAO_PEGTL_NAMESPACE::internal
+{
+   template< typename, typename... >
+   inline constexpr bool has_unwind = false;
+
+   template< typename C, typename... S >
+   inline constexpr bool has_unwind< C, decltype( C::unwind( std::declval< S >()... ) ), S... > = true;
+
+}  // namespace TAO_PEGTL_NAMESPACE::internal
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp b/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
index 416b53d006beb875cf725d25649ceaf8014f26b1..ae2cca18d962d44aa7eb53ea62918fd2a64644d7 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/iterator.hpp
@@ -4,6 +4,7 @@
 #ifndef TAO_PEGTL_INTERNAL_ITERATOR_HPP
 #define TAO_PEGTL_INTERNAL_ITERATOR_HPP
 
+#include <cassert>
 #include <cstdlib>
 
 #include "../config.hpp"
@@ -18,12 +19,15 @@ namespace TAO_PEGTL_NAMESPACE::internal
          : data( in_data )
       {}
 
-      iterator( const char* in_data, const std::size_t in_byte, const std::size_t in_line, const std::size_t in_byte_in_line ) noexcept
+      iterator( const char* in_data, const std::size_t in_byte, const std::size_t in_line, const std::size_t in_column ) noexcept
          : data( in_data ),
            byte( in_byte ),
            line( in_line ),
-           byte_in_line( in_byte_in_line )
-      {}
+           column( in_column )
+      {
+         assert( in_line != 0 );
+         assert( in_column != 0 );
+      }
 
       iterator( const iterator& ) = default;
       iterator( iterator&& ) = default;
@@ -33,16 +37,11 @@ namespace TAO_PEGTL_NAMESPACE::internal
       iterator& operator=( const iterator& ) = default;
       iterator& operator=( iterator&& ) = default;
 
-      void reset() noexcept
-      {
-         *this = iterator();
-      }
-
       const char* data = nullptr;
 
       std::size_t byte = 0;
       std::size_t line = 1;
-      std::size_t byte_in_line = 0;
+      std::size_t column = 1;
    };
 
 }  // namespace TAO_PEGTL_NAMESPACE::internal
diff --git a/packages/PEGTL/include/tao/pegtl/internal/marker.hpp b/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
index 27d4c0cf08e42cccac3502b93685251ce2556fa1..6cec4aa8f6122ff05e1180da6ef8fdcd16e272ac 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/marker.hpp
@@ -46,7 +46,7 @@ namespace TAO_PEGTL_NAMESPACE::internal
       marker( const marker& ) = delete;
       marker( marker&& ) = delete;
 
-      ~marker() noexcept
+      ~marker()
       {
          if( m_input != nullptr ) {
             ( *m_input ) = m_saved;
diff --git a/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp b/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
index 0e3e51127f48a7ef7e6162b3bcb9ed2ec051193a..d1e8f76837a68acff767962ca7c1ef9c9eb92acc 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/missing_apply.hpp
@@ -16,8 +16,11 @@ namespace TAO_PEGTL_NAMESPACE::internal
              typename... States >
    void missing_apply( ParseInput& in, States&&... st )
    {
+      // This function only exists for better error messages, which means that it is only called when we know that it won't compile.
+      // LCOV_EXCL_START
       auto m = in.template mark< rewind_mode::required >();
       (void)Control::template apply< Action >( m.iterator(), in, st... );
+      // LCOV_EXCL_STOP
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE::internal
diff --git a/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp b/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
index 345e50f6e9ec3dce59f0fec4fc0340c48d9aeb86..a7b65f8c925c0b2c174cd406d7edc027dd2257c5 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/missing_apply0.hpp
@@ -15,7 +15,10 @@ namespace TAO_PEGTL_NAMESPACE::internal
              typename... States >
    void missing_apply0( ParseInput& in, States&&... st )
    {
+      // This function only exists for better error messages, which means that it is only called when we know that it won't compile.
+      // LCOV_EXCL_START
       (void)Control::template apply0< Action >( in, st... );
+      // LCOV_EXCL_STOP
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE::internal
diff --git a/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp b/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..04b0774e069b45472b47cce1e4d5d275e19b69ed
--- /dev/null
+++ b/packages/PEGTL/include/tao/pegtl/internal/path_to_string.hpp
@@ -0,0 +1,26 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_INTERNAL_PATH_TO_STRING_HPP
+#define TAO_PEGTL_INTERNAL_PATH_TO_STRING_HPP
+
+#include <filesystem>
+#include <string>
+
+#include "../config.hpp"
+
+namespace TAO_PEGTL_NAMESPACE::internal
+{
+   [[nodiscard]] inline std::string path_to_string( const std::filesystem::path& path )
+   {
+#if defined( __cpp_char8_t )
+      const auto s = path.u8string();
+      return { reinterpret_cast< const char* >( s.data() ), s.size() };
+#else
+      return path.u8string();
+#endif
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE::internal
+
+#endif
diff --git a/packages/PEGTL/include/tao/pegtl/internal/rules.hpp b/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
index 2e9554a70ff1b1818043035f29e944f695ddcb6d..de13adfbe8ff8c967b1d990b62c3ddbf87aa8268 100644
--- a/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
+++ b/packages/PEGTL/include/tao/pegtl/internal/rules.hpp
@@ -5,8 +5,6 @@
 #define TAO_PEGTL_INTERNAL_RULES_HPP
 
 #include "action.hpp"
-#include "alnum.hpp"
-#include "alpha.hpp"
 #include "any.hpp"
 #include "apply.hpp"
 #include "apply0.hpp"
diff --git a/packages/PEGTL/include/tao/pegtl/istream_input.hpp b/packages/PEGTL/include/tao/pegtl/istream_input.hpp
index d9a967ef91f71a5c9e26b61de282db57bb129b31..68a484000fa372b915646e4d7089c85baa35ffa5 100644
--- a/packages/PEGTL/include/tao/pegtl/istream_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/istream_input.hpp
@@ -25,7 +25,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   istream_input( Ts&&... )->istream_input<>;
+   istream_input( Ts&&... ) -> istream_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/match.hpp b/packages/PEGTL/include/tao/pegtl/match.hpp
index 77f596b94c23954b5eb294bfe103686d0aae3434..6bb87565b83dbe01472890412e2861cc0af31bce 100644
--- a/packages/PEGTL/include/tao/pegtl/match.hpp
+++ b/packages/PEGTL/include/tao/pegtl/match.hpp
@@ -13,15 +13,79 @@
 #include "require_apply0.hpp"
 #include "rewind_mode.hpp"
 
-#include "internal/dusel_mode.hpp"
-#include "internal/duseltronik.hpp"
 #include "internal/has_apply.hpp"
 #include "internal/has_apply0.hpp"
+#include "internal/has_unwind.hpp"
+#include "internal/marker.hpp"
 #include "internal/missing_apply.hpp"
 #include "internal/missing_apply0.hpp"
 
+#if defined( _MSC_VER )
+#pragma warning( push )
+#pragma warning( disable : 4702 )
+#endif
+
 namespace TAO_PEGTL_NAMESPACE
 {
+   namespace internal
+   {
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static auto match_no_control( ParseInput& in, States&&... st )
+         -> decltype( Rule::template match< A, M, Action, Control >( in, st... ) )
+      {
+         return Rule::template match< A, M, Action, Control >( in, st... );
+      }
+
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] static auto match_no_control( ParseInput& in, States&&... /*unused*/ )
+         -> decltype( Rule::match( in ) )
+      {
+         return Rule::match( in );
+      }
+
+      template< typename Rule,
+                apply_mode A,
+                rewind_mode M,
+                template< typename... >
+                class Action,
+                template< typename... >
+                class Control,
+                typename ParseInput,
+                typename... States >
+      [[nodiscard]] auto match_control_unwind( ParseInput& in, States&&... st )
+      {
+         if constexpr( has_unwind< Control< Rule >, void, const ParseInput&, States... > ) {
+            try {
+               return match_no_control< Rule, A, M, Action, Control >( in, st... );
+            }
+            catch( ... ) {
+               Control< Rule >::unwind( static_cast< const ParseInput& >( in ), st... );
+               throw;
+            }
+         }
+         else {
+            return match_no_control< Rule, A, M, Action, Control >( in, st... );
+         }
+      }
+
+   }  // namespace internal
+
    template< typename Rule,
              apply_mode A,
              rewind_mode M,
@@ -31,42 +95,75 @@ namespace TAO_PEGTL_NAMESPACE
              class Control,
              typename ParseInput,
              typename... States >
-   [[nodiscard]] bool match( ParseInput& in, States&&... st )
+   [[nodiscard]] auto match( ParseInput& in, States&&... st )
    {
-      constexpr bool enable_control = Control< Rule >::enable;
-      constexpr bool enable_action = enable_control && ( A == apply_mode::action );
+      if constexpr( !Control< Rule >::enable ) {
+         return internal::match_no_control< Rule, A, M, Action, Control >( in, st... );
+      }
+      else {
+         constexpr bool enable_action = ( A == apply_mode::action );
 
-      using iterator_t = typename ParseInput::iterator_t;
-      constexpr bool has_apply_void = enable_action && internal::has_apply< Control< Rule >, void, Action, const iterator_t&, const ParseInput&, States... >;
-      constexpr bool has_apply_bool = enable_action && internal::has_apply< Control< Rule >, bool, Action, const iterator_t&, const ParseInput&, States... >;
-      constexpr bool has_apply = has_apply_void || has_apply_bool;
+         using iterator_t = typename ParseInput::iterator_t;
+         constexpr bool has_apply_void = enable_action && internal::has_apply< Control< Rule >, void, Action, const iterator_t&, const ParseInput&, States... >;
+         constexpr bool has_apply_bool = enable_action && internal::has_apply< Control< Rule >, bool, Action, const iterator_t&, const ParseInput&, States... >;
+         constexpr bool has_apply = has_apply_void || has_apply_bool;
 
-      constexpr bool has_apply0_void = enable_action && internal::has_apply0< Control< Rule >, void, Action, const ParseInput&, States... >;
-      constexpr bool has_apply0_bool = enable_action && internal::has_apply0< Control< Rule >, bool, Action, const ParseInput&, States... >;
-      constexpr bool has_apply0 = has_apply0_void || has_apply0_bool;
+         constexpr bool has_apply0_void = enable_action && internal::has_apply0< Control< Rule >, void, Action, const ParseInput&, States... >;
+         constexpr bool has_apply0_bool = enable_action && internal::has_apply0< Control< Rule >, bool, Action, const ParseInput&, States... >;
+         constexpr bool has_apply0 = has_apply0_void || has_apply0_bool;
 
-      static_assert( !( has_apply && has_apply0 ), "both apply() and apply0() defined" );
+         static_assert( !( has_apply && has_apply0 ), "both apply() and apply0() defined" );
 
-      constexpr bool is_nothing = std::is_base_of_v< nothing< Rule >, Action< Rule > >;
-      static_assert( !( has_apply && is_nothing ), "unexpected apply() defined" );
-      static_assert( !( has_apply0 && is_nothing ), "unexpected apply0() defined" );
+         constexpr bool is_nothing = std::is_base_of_v< nothing< Rule >, Action< Rule > >;
+         static_assert( !( has_apply && is_nothing ), "unexpected apply() defined" );
+         static_assert( !( has_apply0 && is_nothing ), "unexpected apply0() defined" );
 
-      if constexpr( !has_apply && std::is_base_of_v< require_apply, Action< Rule > > ) {
-         internal::missing_apply< Control< Rule >, Action >( in, st... );
-      }
+         if constexpr( !has_apply && std::is_base_of_v< require_apply, Action< Rule > > ) {
+            internal::missing_apply< Control< Rule >, Action >( in, st... );
+         }
 
-      if constexpr( !has_apply0 && std::is_base_of_v< require_apply0, Action< Rule > > ) {
-         internal::missing_apply0< Control< Rule >, Action >( in, st... );
-      }
+         if constexpr( !has_apply0 && std::is_base_of_v< require_apply0, Action< Rule > > ) {
+            internal::missing_apply0< Control< Rule >, Action >( in, st... );
+         }
 
-      constexpr bool validate_nothing = std::is_base_of_v< maybe_nothing, Action< void > >;
-      constexpr bool is_maybe_nothing = std::is_base_of_v< maybe_nothing, Action< Rule > >;
-      static_assert( !enable_action || !validate_nothing || is_nothing || is_maybe_nothing || has_apply || has_apply0, "either apply() or apply0() must be defined" );
+         constexpr bool validate_nothing = std::is_base_of_v< maybe_nothing, Action< void > >;
+         constexpr bool is_maybe_nothing = std::is_base_of_v< maybe_nothing, Action< Rule > >;
+         static_assert( !enable_action || !validate_nothing || is_nothing || is_maybe_nothing || has_apply || has_apply0, "either apply() or apply0() must be defined" );
 
-      constexpr auto mode = static_cast< internal::dusel_mode >( enable_control + has_apply_void + 2 * has_apply_bool + 3 * has_apply0_void + 4 * has_apply0_bool );
-      return internal::duseltronik< Rule, A, M, Action, Control, mode >::match( in, st... );
+         constexpr bool use_marker = has_apply || has_apply0_bool;
+
+         auto m = in.template mark< ( use_marker ? rewind_mode::required : rewind_mode::dontcare ) >();
+         Control< Rule >::start( static_cast< const ParseInput& >( in ), st... );
+         auto result = internal::match_control_unwind< Rule, A, ( use_marker ? rewind_mode::active : M ), Action, Control >( in, st... );
+         if( result ) {
+            if constexpr( has_apply_void ) {
+               Control< Rule >::template apply< Action >( m.iterator(), static_cast< const ParseInput& >( in ), st... );
+            }
+            else if constexpr( has_apply_bool ) {
+               result = Control< Rule >::template apply< Action >( m.iterator(), static_cast< const ParseInput& >( in ), st... );
+            }
+            else if constexpr( has_apply0_void ) {
+               Control< Rule >::template apply0< Action >( static_cast< const ParseInput& >( in ), st... );
+            }
+            else if constexpr( has_apply0_bool ) {
+               result = Control< Rule >::template apply0< Action >( static_cast< const ParseInput& >( in ), st... );
+            }
+         }
+         if( result ) {
+            Control< Rule >::success( static_cast< const ParseInput& >( in ), st... );
+         }
+         else {
+            Control< Rule >::failure( static_cast< const ParseInput& >( in ), st... );
+         }
+         (void)m( result );
+         return result;
+      }
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
+#if defined( _MSC_VER )
+#pragma warning( pop )
+#endif
+
 #endif
diff --git a/packages/PEGTL/include/tao/pegtl/memory_input.hpp b/packages/PEGTL/include/tao/pegtl/memory_input.hpp
index 6c4c8c9a5f21944bbe74b36361126b413d8d93ba..6f303f57c002f8dc936c6ca9ec2066c338579bf4 100644
--- a/packages/PEGTL/include/tao/pegtl/memory_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/memory_input.hpp
@@ -4,6 +4,7 @@
 #ifndef TAO_PEGTL_MEMORY_INPUT_HPP
 #define TAO_PEGTL_MEMORY_INPUT_HPP
 
+#include <cassert>
 #include <cstddef>
 #include <cstdint>
 #include <cstring>
@@ -89,9 +90,9 @@ namespace TAO_PEGTL_NAMESPACE
             return m_current.line;
          }
 
-         [[nodiscard]] std::size_t byte_in_line() const noexcept
+         [[nodiscard]] std::size_t column() const noexcept
          {
-            return m_current.byte_in_line;
+            return m_current.column;
          }
 
          void bump( const std::size_t in_count = 1 ) noexcept
@@ -114,12 +115,15 @@ namespace TAO_PEGTL_NAMESPACE
             return TAO_PEGTL_NAMESPACE::position( it, m_source );
          }
 
-         void restart( const std::size_t in_byte = 0, const std::size_t in_line = 1, const std::size_t in_byte_in_line = 0 )
+         void restart( const std::size_t in_byte = 0, const std::size_t in_line = 1, const std::size_t in_column = 1 )
          {
+            assert( in_line != 0 );
+            assert( in_column != 0 );
+
             m_current.data = m_begin;
             m_current.byte = in_byte;
             m_current.line = in_line;
-            m_current.byte_in_line = in_byte_in_line;
+            m_current.column = in_column;
          }
 
       protected:
@@ -255,8 +259,8 @@ namespace TAO_PEGTL_NAMESPACE
       {}
 
       template< typename T >
-      memory_input( const char* in_begin, const char* in_end, T&& in_source, const std::size_t in_byte, const std::size_t in_line, const std::size_t in_byte_in_line ) noexcept( std::is_nothrow_constructible_v< Source, T&& > )
-         : memory_input( { in_begin, in_byte, in_line, in_byte_in_line }, in_end, std::forward< T >( in_source ) )
+      memory_input( const char* in_begin, const char* in_end, T&& in_source, const std::size_t in_byte, const std::size_t in_line, const std::size_t in_column ) noexcept( std::is_nothrow_constructible_v< Source, T&& > )
+         : memory_input( { in_begin, in_byte, in_line, in_column }, in_end, std::forward< T >( in_source ) )
       {}
 
       memory_input( const memory_input& ) = delete;
@@ -334,7 +338,7 @@ namespace TAO_PEGTL_NAMESPACE
 
       [[nodiscard]] const char* begin_of_line( const TAO_PEGTL_NAMESPACE::position& p ) const noexcept
       {
-         return at( p ) - p.byte_in_line;
+         return at( p ) - ( p.column - 1 );
       }
 
       [[nodiscard]] const char* end_of_line( const TAO_PEGTL_NAMESPACE::position& p ) const noexcept
@@ -354,7 +358,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   memory_input( Ts&&... )->memory_input<>;
+   memory_input( Ts&&... ) -> memory_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/mmap_input.hpp b/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
index 82206dee5ce1e351acd426dcf9a1836b2d511266..acc24bbca28508ba862aa17501dd060cb59bc57e 100644
--- a/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/mmap_input.hpp
@@ -4,14 +4,16 @@
 #ifndef TAO_PEGTL_MMAP_INPUT_HPP
 #define TAO_PEGTL_MMAP_INPUT_HPP
 
+#include <filesystem>
 #include <string>
-#include <utility>
 
 #include "config.hpp"
 #include "eol.hpp"
 #include "memory_input.hpp"
 #include "tracking_mode.hpp"
 
+#include "internal/path_to_string.hpp"
+
 #if defined( __unix__ ) || ( defined( __APPLE__ ) && defined( __MACH__ ) )
 #include <unistd.h>  // Required for _POSIX_MAPPED_FILES
 #endif
@@ -29,13 +31,10 @@ namespace TAO_PEGTL_NAMESPACE
    {
       struct mmap_holder
       {
-         const std::string filename;
          const file_mapper data;
 
-         template< typename T >
-         explicit mmap_holder( T&& in_filename )
-            : filename( std::forward< T >( in_filename ) ),
-              data( filename.c_str() )
+         explicit mmap_holder( const std::filesystem::path& path )
+            : data( path )
          {}
 
          mmap_holder( const mmap_holder& ) = delete;
@@ -52,12 +51,15 @@ namespace TAO_PEGTL_NAMESPACE
    template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf >
    struct mmap_input
       : private internal::mmap_holder,
-        public memory_input< P, Eol, const char* >
+        public memory_input< P, Eol >
    {
-      template< typename T >
-      explicit mmap_input( T&& in_filename )
-         : internal::mmap_holder( std::forward< T >( in_filename ) ),
-           memory_input< P, Eol, const char* >( data.begin(), data.end(), filename.c_str() )
+      mmap_input( const std::filesystem::path& path, const std::string& source )
+         : internal::mmap_holder( path ),
+           memory_input< P, Eol >( data.begin(), data.end(), source )
+      {}
+
+      explicit mmap_input( const std::filesystem::path& path )
+         : mmap_input( path, internal::path_to_string( path ) )
       {}
 
       mmap_input( const mmap_input& ) = delete;
@@ -70,7 +72,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   explicit mmap_input( Ts&&... )->mmap_input<>;
+   explicit mmap_input( Ts&&... ) -> mmap_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/must_if.hpp b/packages/PEGTL/include/tao/pegtl/must_if.hpp
index ab15cb6b6c71fbd0f287b3e5edda55c9ded4313e..d229ae5711fc221a8e554f1129d595ab5519bca7 100644
--- a/packages/PEGTL/include/tao/pegtl/must_if.hpp
+++ b/packages/PEGTL/include/tao/pegtl/must_if.hpp
@@ -4,6 +4,8 @@
 #ifndef TAO_PEGTL_MUST_IF_HPP
 #define TAO_PEGTL_MUST_IF_HPP
 
+#include <type_traits>
+
 #include "config.hpp"
 #include "normal.hpp"
 
@@ -11,15 +13,15 @@ namespace TAO_PEGTL_NAMESPACE
 {
    namespace internal
    {
-      template< typename T, typename Rule, typename = void >
-      inline constexpr bool raise_on_failure = ( T::template message< Rule > != nullptr );
+      template< typename Errors, typename Rule, typename = void >
+      inline constexpr bool raise_on_failure = ( Errors::template message< Rule > != nullptr );
 
-      template< typename T, typename Rule >
-      inline constexpr bool raise_on_failure< T, Rule, decltype( T::template raise_on_failure< Rule >, void() ) > = T::template raise_on_failure< Rule >;
+      template< typename Errors, typename Rule >
+      inline constexpr bool raise_on_failure< Errors, Rule, std::void_t< decltype( Errors::template raise_on_failure< Rule > ) > > = Errors::template raise_on_failure< Rule >;
 
    }  // namespace internal
 
-   template< typename T, template< typename... > class Base = normal, bool RequireMessage = true >
+   template< typename Errors, template< typename... > class Base = normal, bool RequireMessage = true >
    struct must_if
    {
       template< typename Rule >
@@ -27,9 +29,9 @@ namespace TAO_PEGTL_NAMESPACE
          : Base< Rule >
       {
          template< typename ParseInput, typename... States >
-         static void failure( const ParseInput& in, States&&... st ) noexcept( !internal::raise_on_failure< T, Rule > && noexcept( Base< Rule >::failure( in, st... ) ) )
+         static void failure( const ParseInput& in, States&&... st ) noexcept( noexcept( Base< Rule >::failure( in, st... ) ) && !internal::raise_on_failure< Errors, Rule > )
          {
-            if constexpr( internal::raise_on_failure< T, Rule > ) {
+            if constexpr( internal::raise_on_failure< Errors, Rule > ) {
                raise( in, st... );
             }
             else {
@@ -38,16 +40,16 @@ namespace TAO_PEGTL_NAMESPACE
          }
 
          template< typename ParseInput, typename... States >
-         [[noreturn]] static void raise( const ParseInput& in, States&&... st )
+         [[noreturn]] static void raise( const ParseInput& in, [[maybe_unused]] States&&... st )
          {
             if constexpr( RequireMessage ) {
-               static_assert( T::template message< Rule > != nullptr );
+               static_assert( Errors::template message< Rule > != nullptr );
             }
-            if constexpr( T::template message< Rule > != nullptr ) {
-               constexpr const char* p = T::template message< Rule >;
+            if constexpr( Errors::template message< Rule > != nullptr ) {
+               constexpr const char* p = Errors::template message< Rule >;
                throw parse_error( p, in );
 #if defined( _MSC_VER )
-               (void)( (void)st, ... );
+               ( (void)st, ... );
 #endif
             }
             else {
diff --git a/packages/PEGTL/include/tao/pegtl/normal.hpp b/packages/PEGTL/include/tao/pegtl/normal.hpp
index 5d7845b99433dd041d51ef05b1740d392f12dab7..dcb9e4d9430137ba2c5f9822d21e9ac8ae8a999a 100644
--- a/packages/PEGTL/include/tao/pegtl/normal.hpp
+++ b/packages/PEGTL/include/tao/pegtl/normal.hpp
@@ -10,11 +10,11 @@
 
 #include "apply_mode.hpp"
 #include "config.hpp"
+#include "demangle.hpp"
 #include "match.hpp"
 #include "parse_error.hpp"
 #include "rewind_mode.hpp"
 
-#include "internal/demangle.hpp"
 #include "internal/enable_control.hpp"
 #include "internal/has_match.hpp"
 
@@ -40,7 +40,7 @@ namespace TAO_PEGTL_NAMESPACE
       template< typename ParseInput, typename... States >
       [[noreturn]] static void raise( const ParseInput& in, States&&... /*unused*/ )
       {
-         throw parse_error( "parse error matching " + std::string( internal::demangle< Rule >() ), in );
+         throw parse_error( "parse error matching " + std::string( demangle< Rule >() ), in );
       }
 
       template< template< typename... > class Action,
diff --git a/packages/PEGTL/include/tao/pegtl/parse.hpp b/packages/PEGTL/include/tao/pegtl/parse.hpp
index 7d435708a06209082cca146350cd993c218acf70..02528a032f5eab1c3a85911c1dea0a622e907e46 100644
--- a/packages/PEGTL/include/tao/pegtl/parse.hpp
+++ b/packages/PEGTL/include/tao/pegtl/parse.hpp
@@ -22,7 +22,7 @@ namespace TAO_PEGTL_NAMESPACE
              rewind_mode M = rewind_mode::required,
              typename ParseInput,
              typename... States >
-   bool parse( ParseInput&& in, States&&... st )
+   auto parse( ParseInput&& in, States&&... st )
    {
       return Control< Rule >::template match< A, M, Action, Control >( in, st... );
    }
@@ -35,13 +35,13 @@ namespace TAO_PEGTL_NAMESPACE
              typename OuterInput,
              typename ParseInput,
              typename... States >
-   bool parse_nested( const OuterInput& oi, ParseInput&& in, States&&... st )
+   auto parse_nested( const OuterInput& oi, ParseInput&& in, States&&... st )
    {
       try {
          return parse< Rule, Action, Control, A, M >( in, st... );
       }
       catch( parse_error& e ) {
-         e.positions.push_back( oi.position() );
+         e.add_position( oi.position() );
          throw;
       }
    }
diff --git a/packages/PEGTL/include/tao/pegtl/parse_error.hpp b/packages/PEGTL/include/tao/pegtl/parse_error.hpp
index d48f95b830048929c62fbaba1348be9be915b721..b2f8741ff7ded4dc6343ec68276761eaee830947 100644
--- a/packages/PEGTL/include/tao/pegtl/parse_error.hpp
+++ b/packages/PEGTL/include/tao/pegtl/parse_error.hpp
@@ -4,10 +4,11 @@
 #ifndef TAO_PEGTL_PARSE_ERROR_HPP
 #define TAO_PEGTL_PARSE_ERROR_HPP
 
-#include <ostream>
-#include <sstream>
+#include <cstddef>
+#include <memory>
 #include <stdexcept>
 #include <string>
+#include <string_view>
 #include <utility>
 #include <vector>
 
@@ -16,50 +17,97 @@
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   struct parse_error
-      : std::runtime_error
+   namespace internal
    {
-      template< typename Msg >
-      parse_error( Msg&& msg, std::vector< position > in_positions )
-         : std::runtime_error( std::forward< Msg >( msg ) ),
-           positions( std::move( in_positions ) )
-      {}
+      class parse_error
+      {
+      private:
+         std::string m_msg;
+         std::size_t m_prefix = 0;
+         std::vector< position > m_positions;
 
-      template< typename Msg >
-      parse_error( Msg&& msg, const position& pos )
-         : std::runtime_error( std::forward< Msg >( msg ) ),
-           positions( 1, pos )
-      {}
+      public:
+         explicit parse_error( const char* msg )
+            : m_msg( msg )
+         {}
+
+         [[nodiscard]] const char* what() const noexcept
+         {
+            return m_msg.c_str();
+         }
+
+         [[nodiscard]] std::string_view message() const noexcept
+         {
+            return { m_msg.data() + m_prefix, m_msg.size() - m_prefix };
+         }
+
+         [[nodiscard]] const std::vector< position >& positions() const noexcept
+         {
+            return m_positions;
+         }
+
+         void add_position( position&& p )
+         {
+            const auto prefix = to_string( p );
+            m_msg = prefix + ": " + m_msg;
+            m_prefix += prefix.size() + 2;
+            m_positions.emplace_back( std::move( p ) );
+         }
+      };
 
-      template< typename Msg >
-      parse_error( Msg&& msg, position&& pos )
-         : std::runtime_error( std::forward< Msg >( msg ) )
+   }  // namespace internal
+
+   class parse_error
+      : public std::runtime_error
+   {
+   private:
+      std::shared_ptr< internal::parse_error > m_impl;
+
+   public:
+      parse_error( const char* msg, position p )
+         : std::runtime_error( msg ),
+           m_impl( std::make_shared< internal::parse_error >( msg ) )
       {
-         positions.emplace_back( std::move( pos ) );
+         m_impl->add_position( std::move( p ) );
       }
 
-      template< typename Msg, typename ParseInput >
-      parse_error( Msg&& msg, const ParseInput& in )
-         : parse_error( std::forward< Msg >( msg ), in.position() )
+      parse_error( const std::string& msg, position p )
+         : parse_error( msg.c_str(), std::move( p ) )
       {}
 
-      std::vector< position > positions;
-   };
+      template< typename ParseInput >
+      parse_error( const char* msg, const ParseInput& in )
+         : parse_error( msg, in.position() )
+      {}
 
-   inline std::ostream& operator<<( std::ostream& o, const parse_error& e )
-   {
-      for( auto it = e.positions.rbegin(); it != e.positions.rend(); ++it ) {
-         o << *it << ": ";
+      template< typename ParseInput >
+      parse_error( const std::string& msg, const ParseInput& in )
+         : parse_error( msg.c_str(), in.position() )
+      {}
+
+      [[nodiscard]] const char* what() const noexcept override
+      {
+         return m_impl->what();
       }
-      return o << e.what();
-   }
 
-   [[nodiscard]] inline std::string to_string( const parse_error& e )
-   {
-      std::ostringstream o;
-      o << e;
-      return o.str();
-   }
+      [[nodiscard]] std::string_view message() const noexcept
+      {
+         return m_impl->message();
+      }
+
+      [[nodiscard]] const std::vector< position >& positions() const noexcept
+      {
+         return m_impl->positions();
+      }
+
+      void add_position( position&& p )
+      {
+         if( m_impl.use_count() > 1 ) {
+            m_impl = std::make_shared< internal::parse_error >( *m_impl );
+         }
+         m_impl->add_position( std::move( p ) );
+      }
+   };
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/position.hpp b/packages/PEGTL/include/tao/pegtl/position.hpp
index 6cdc07bd02077901d81769ab60ff3f573b187190..7543fdb0ecbee4471381d9640bd0dadc35bd0369 100644
--- a/packages/PEGTL/include/tao/pegtl/position.hpp
+++ b/packages/PEGTL/include/tao/pegtl/position.hpp
@@ -23,7 +23,7 @@ namespace TAO_PEGTL_NAMESPACE
       position( position&& p ) noexcept
          : byte( p.byte ),
            line( p.line ),
-           byte_in_line( p.byte_in_line ),
+           column( p.column ),
            source( std::move( p.source ) )
       {}
 
@@ -33,7 +33,7 @@ namespace TAO_PEGTL_NAMESPACE
       {
          byte = p.byte;
          line = p.line;
-         byte_in_line = p.byte_in_line;
+         column = p.column;
          source = std::move( p.source );
          return *this;
       }
@@ -44,7 +44,7 @@ namespace TAO_PEGTL_NAMESPACE
       position( const internal::iterator& in_iter, T&& in_source )
          : byte( in_iter.byte ),
            line( in_iter.line ),
-           byte_in_line( in_iter.byte_in_line ),
+           column( in_iter.column ),
            source( std::forward< T >( in_source ) )
       {}
 
@@ -52,13 +52,23 @@ namespace TAO_PEGTL_NAMESPACE
 
       std::size_t byte;
       std::size_t line;
-      std::size_t byte_in_line;
+      std::size_t column;
       std::string source;
    };
 
-   inline std::ostream& operator<<( std::ostream& o, const position& p )
+   inline bool operator==( const position& lhs, const position& rhs ) noexcept
    {
-      return o << p.source << ':' << p.line << ':' << p.byte_in_line << '(' << p.byte << ')';
+      return ( lhs.byte == rhs.byte ) && ( lhs.source == rhs.source );
+   }
+
+   inline bool operator!=( const position& lhs, const position& rhs ) noexcept
+   {
+      return !( lhs == rhs );
+   }
+
+   inline std::ostream& operator<<( std::ostream& os, const position& p )
+   {
+      return os << p.source << ':' << p.line << ':' << p.column;
    }
 
    [[nodiscard]] inline std::string to_string( const position& p )
diff --git a/packages/PEGTL/include/tao/pegtl/read_input.hpp b/packages/PEGTL/include/tao/pegtl/read_input.hpp
index 224b2e9a75874cbac59aff00dbe95b950b29d241..f830de3b90bad0e4f56fcf966da7933ba3206520 100644
--- a/packages/PEGTL/include/tao/pegtl/read_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/read_input.hpp
@@ -4,6 +4,7 @@
 #ifndef TAO_PEGTL_READ_INPUT_HPP
 #define TAO_PEGTL_READ_INPUT_HPP
 
+#include <filesystem>
 #include <string>
 
 #include "config.hpp"
@@ -12,46 +13,28 @@
 #include "tracking_mode.hpp"
 
 #include "internal/file_reader.hpp"
+#include "internal/path_to_string.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   namespace internal
-   {
-      struct filename_holder
-      {
-         const std::string filename;
-
-         template< typename T >
-         explicit filename_holder( T&& in_filename )
-            : filename( std::forward< T >( in_filename ) )
-         {}
-
-         filename_holder( const filename_holder& ) = delete;
-         filename_holder( filename_holder&& ) = delete;
-
-         ~filename_holder() = default;
-
-         void operator=( const filename_holder& ) = delete;
-         void operator=( filename_holder&& ) = delete;
-      };
-
-   }  // namespace internal
-
    template< tracking_mode P = tracking_mode::eager, typename Eol = eol::lf_crlf >
    struct read_input
-      : private internal::filename_holder,
-        public string_input< P, Eol, const char* >
+      : string_input< P, Eol >
    {
-      template< typename T >
-      explicit read_input( T&& in_filename )
-         : internal::filename_holder( std::forward< T >( in_filename ) ),
-           string_input< P, Eol, const char* >( internal::file_reader( filename.c_str() ).read(), filename.c_str() )
+      read_input( const std::filesystem::path& path, const std::string& source )
+         : string_input< P, Eol >( internal::file_reader( path ).read(), source )
+      {}
+
+      explicit read_input( const std::filesystem::path& path )
+         : read_input( path, internal::path_to_string( path ) )
+      {}
+
+      read_input( FILE* file, const std::filesystem::path& path, const std::string& source )
+         : string_input< P, Eol >( internal::file_reader( file, path ).read(), source )
       {}
 
-      template< typename T >
-      read_input( FILE* in_file, T&& in_filename )
-         : internal::filename_holder( std::forward< T >( in_filename ) ),
-           string_input< P, Eol, const char* >( internal::file_reader( in_file, filename.c_str() ).read(), filename.c_str() )
+      read_input( FILE* file, const std::filesystem::path& path )
+         : read_input( file, path, internal::path_to_string( path ) )
       {}
 
       read_input( const read_input& ) = delete;
@@ -64,7 +47,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   explicit read_input( Ts&&... )->read_input<>;
+   explicit read_input( Ts&&... ) -> read_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/include/tao/pegtl/rules.hpp b/packages/PEGTL/include/tao/pegtl/rules.hpp
index 659619c0eda2725b8bf3d6d67ae1fb912ba17249..09f7ad5d188c881fc8ea08e020cc8b8d2cd98d6b 100644
--- a/packages/PEGTL/include/tao/pegtl/rules.hpp
+++ b/packages/PEGTL/include/tao/pegtl/rules.hpp
@@ -12,9 +12,9 @@
 namespace TAO_PEGTL_NAMESPACE
 {
    // clang-format off
+   template< template< typename... > class Action, typename... Rules > struct action : internal::action< Action, Rules... > {};
    template< typename... Actions > struct apply : internal::apply< Actions... > {};
    template< typename... Actions > struct apply0 : internal::apply0< Actions... > {};
-   template< template< typename... > class Action, typename... Rules > struct action : internal::action< Action, Rules... > {};
    template< typename... Rules > struct at : internal::at< Rules... > {};
    struct bof : internal::bof {};
    struct bol : internal::bol {};
@@ -58,7 +58,7 @@ namespace TAO_PEGTL_NAMESPACE
    template< typename Cond, typename... Rules > struct star_must : internal::star_must< Cond, Rules... > {};
    template< typename State, typename... Rules > struct state : internal::state< State, Rules... > {};
    struct success : internal::success {};
-   template< typename... Rules > struct try_catch : internal::seq< internal::try_catch_type< parse_error, Rules... > > {};
+   template< typename... Rules > struct try_catch : internal::try_catch_type< parse_error, Rules... > {};
    template< typename Exception, typename... Rules > struct try_catch_type : internal::seq< internal::try_catch_type< Exception, Rules... > > {};
    template< typename Cond, typename... Rules > struct until : internal::until< Cond, Rules... > {};
    // clang-format on
diff --git a/packages/PEGTL/include/tao/pegtl/string_input.hpp b/packages/PEGTL/include/tao/pegtl/string_input.hpp
index 06fec70d670f3554e5f3f167d33416048b81ef41..8bacffbc4d1daffb98a69193a6330aae57b580e4 100644
--- a/packages/PEGTL/include/tao/pegtl/string_input.hpp
+++ b/packages/PEGTL/include/tao/pegtl/string_input.hpp
@@ -57,7 +57,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template< typename... Ts >
-   explicit string_input( Ts&&... )->string_input<>;
+   explicit string_input( Ts&&... ) -> string_input<>;
 
 }  // namespace TAO_PEGTL_NAMESPACE
 
diff --git a/packages/PEGTL/src/example/pegtl/CMakeLists.txt b/packages/PEGTL/src/example/pegtl/CMakeLists.txt
index 60518785710ccf34241b761ef4dfef3d6d6c4a52..0cbdd6288262e34785bd5aef39dff669aeb7d8e2 100644
--- a/packages/PEGTL/src/example/pegtl/CMakeLists.txt
+++ b/packages/PEGTL/src/example/pegtl/CMakeLists.txt
@@ -10,13 +10,19 @@ set(example_sources
   dynamic_match.cpp
   hello_world.cpp
   indent_aware.cpp
+  json_analyze.cpp
+  json_ast.cpp
   json_build.cpp
   json_count.cpp
   json_coverage.cpp
   json_parse.cpp
-  json_print_rules.cpp
-  json_print_sub_rules.cpp
+  json_print_debug.cpp
+  json_print_names.cpp
+  json_trace.cpp
+  lua53_analyze.cpp
   lua53_parse.cpp
+  lua53_print_debug.cpp
+  lua53_print_names.cpp
   modulus_match.cpp
   parse_tree.cpp
   parse_tree_user_state.cpp
@@ -27,6 +33,8 @@ set(example_sources
   symbol_table.cpp
   unescape.cpp
   uri.cpp
+  uri_print_debug.cpp
+  uri_print_names.cpp
   uri_trace.cpp
 )
 
diff --git a/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp b/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
index 5af83c55112402a143afa902ce883e6b5bf2ce49..b019047c937ae57c44cfa5fbb2599f26cbbfd8bb 100644
--- a/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
+++ b/packages/PEGTL/src/example/pegtl/abnf2pegtl.cpp
@@ -2,6 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include <algorithm>
+#include <iomanip>
 #include <iostream>
 #include <iterator>
 #include <map>
@@ -25,7 +26,6 @@
 
 #include <tao/pegtl.hpp>
 #include <tao/pegtl/contrib/abnf.hpp>
-#include <tao/pegtl/contrib/analyze.hpp>
 #include <tao/pegtl/contrib/parse_tree.hpp>
 
 namespace TAO_PEGTL_NAMESPACE
@@ -145,7 +145,7 @@ namespace TAO_PEGTL_NAMESPACE
 
          rules_t::reverse_iterator find_rule( rules_t& r, const std::string& v, const rules_t::reverse_iterator& rbegin )
          {
-            return std::find_if( rbegin, r.rend(), [&]( const rules_t::value_type& p ) { return TAO_PEGTL_STRCASECMP( p.c_str(), v.c_str() ) == 0; } );
+            return std::find_if( rbegin, r.rend(), [ & ]( const rules_t::value_type& p ) { return TAO_PEGTL_STRCASECMP( p.c_str(), v.c_str() ) == 0; } );
          }
 
          rules_t::reverse_iterator find_rule( rules_t& r, const std::string& v )
@@ -180,7 +180,7 @@ namespace TAO_PEGTL_NAMESPACE
          {
             it.data += delta;
             it.byte += delta;
-            it.byte_in_line += delta;
+            it.column += delta;
          }
 
       }  // namespace
@@ -535,7 +535,7 @@ namespace TAO_PEGTL_NAMESPACE
          template< typename T >
          void add( const function_t& f )
          {
-            map_.try_emplace( internal::demangle< T >(), f );
+            map_.try_emplace( demangle< T >(), f );
          }
 
          std::string operator()( const node_ptr& n ) const
@@ -717,14 +717,10 @@ int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
    using namespace TAO_PEGTL_NAMESPACE;
 
    if( argc != 2 ) {
-      std::cerr << "Usage: " << argv[ 0 ] << " SOURCE" << std::endl;
+      std::cerr << "Usage: " << argv[ 0 ] << " SOURCE\n";
       return 1;
    }
 
-   if( analyze< abnf::grammar::rulelist >() != 0 ) {
-      return 2;
-   }
-
    file_input in( argv[ 1 ] );
    try {
       const auto root = parse_tree::parse< abnf::grammar::rulelist, abnf::selector, nothing, abnf::control >( in );
@@ -734,14 +730,14 @@ int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
       }
 
       for( const auto& rule : root->children ) {
-         std::cout << abnf::to_string( rule ) << std::endl;
+         std::cout << abnf::to_string( rule ) << '\n';
       }
    }
    catch( const parse_error& e ) {
-      const auto p = e.positions.front();
-      std::cerr << e.what() << std::endl
-                << in.line_at( p ) << std::endl
-                << std::string( p.byte_in_line, ' ' ) << '^' << std::endl;
+      const auto p = e.positions().front();
+      std::cerr << e.what() << '\n'
+                << in.line_at( p ) << '\n'
+                << std::setw( p.column ) << '^' << '\n';
    }
 
    return 0;
diff --git a/packages/PEGTL/src/example/pegtl/dynamic_match.cpp b/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
index da9f2dc24623dc5ce150b1847d1347c994aecc1f..1f35ee9ec3d15e86bc80f6de9faa3eb8443355c7 100644
--- a/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
+++ b/packages/PEGTL/src/example/pegtl/dynamic_match.cpp
@@ -96,8 +96,10 @@ namespace TAO_PEGTL_NAMESPACE
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
-   const auto issues = pegtl::analyze< dynamic::grammar >();
-   assert( !issues );
+   if( pegtl::analyze< dynamic::grammar >() != 0 ) {
+      std::cerr << "cycles without progress detected!" << std::endl;
+      return 1;
+   }
 
    if( argc > 1 ) {
       std::string id;
diff --git a/packages/PEGTL/src/example/pegtl/json_analyze.cpp b/packages/PEGTL/src/example/pegtl/json_analyze.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bebef24754afbd5eb1a6c41032ca067c882c347d
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/json_analyze.cpp
@@ -0,0 +1,25 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/analyze.hpp>
+#include <tao/pegtl/contrib/json.hpp>
+
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+namespace example
+{
+   using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+}  // namespace example
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   if( pegtl::analyze< example::grammar >() != 0 ) {
+      std::cerr << "cycles without progress detected!" << std::endl;
+      return 1;
+   }
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/json_ast.cpp b/packages/PEGTL/src/example/pegtl/json_ast.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ecc8e57a9fa8244cbaa060d8996026999b3d0297
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/json_ast.cpp
@@ -0,0 +1,60 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iomanip>
+#include <iostream>
+
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/json.hpp>
+#include <tao/pegtl/contrib/parse_tree.hpp>
+#include <tao/pegtl/contrib/parse_tree_to_dot.hpp>
+
+#include "json_errors.hpp"
+
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+namespace example
+{
+   using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+   template< typename Rule >
+   using selector = pegtl::parse_tree::selector<
+      Rule,
+      pegtl::parse_tree::remove_content::on<
+         pegtl::json::null,
+         pegtl::json::true_,
+         pegtl::json::false_,
+         pegtl::json::array,
+         pegtl::json::object,
+         pegtl::json::member >,
+      pegtl::parse_tree::store_content::on<
+         pegtl::json::number,
+         pegtl::json::string,
+         pegtl::json::key > >;
+
+}  // namespace example
+
+int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
+{
+   if( argc != 2 ) {
+      std::cerr << "Usage: " << argv[ 0 ] << " JSON\n"
+                << "Generate a 'dot' file from the JSON text.\n\n"
+                << "Example: " << argv[ 0 ] << " \"{\"foo\":[42,null]}\" | dot -Tpng -o parse_tree.png\n";
+      return 1;
+   }
+
+   pegtl::argv_input in( argv, 1 );
+   try {
+      const auto root = pegtl::parse_tree::parse< example::grammar, example::selector, pegtl::nothing, example::control >( in );
+      pegtl::parse_tree::print_dot( std::cout, *root );
+   }
+   catch( const pegtl::parse_error& e ) {
+      const auto p = e.positions().front();
+      std::cerr << e.what() << std::endl
+                << in.line_at( p ) << std::endl
+                << std::setw( p.column ) << '^' << std::endl;
+      return 1;
+   }
+
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/json_build.cpp b/packages/PEGTL/src/example/pegtl/json_build.cpp
index f4ec3dc9f61b476969531ea45647a23d609fb7f0..fe8f06f44d1728529c4b34aa9d4aed0f6be572d1 100644
--- a/packages/PEGTL/src/example/pegtl/json_build.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_build.cpp
@@ -14,7 +14,7 @@
 
 namespace pegtl = TAO_PEGTL_NAMESPACE;
 
-namespace examples
+namespace example
 {
    // State class that stores the result of a JSON parsing run -- a single JSON object.
    // The other members are used temporarily, at the end of a (successful) parsing run.
@@ -157,7 +157,7 @@ namespace examples
 
    using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
 
-}  // namespace examples
+}  // namespace example
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
@@ -165,9 +165,9 @@ int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
       std::cerr << "usage: " << argv[ 0 ] << " <json>";
    }
    else {
-      examples::json_state state;
+      example::json_state state;
       pegtl::file_input in( argv[ 1 ] );
-      pegtl::parse< examples::grammar, examples::action, examples::control >( in, state );
+      pegtl::parse< example::grammar, example::action, example::control >( in, state );
       assert( state.keys.empty() );
       assert( state.arrays.empty() );
       assert( state.objects.empty() );
diff --git a/packages/PEGTL/src/example/pegtl/json_classes.hpp b/packages/PEGTL/src/example/pegtl/json_classes.hpp
index 8513ec804d8aa2d6913778467bc676bbec8a40a8..9b0c8bb1eb9cf574b80fdd1878e18ff0472eaaaf 100644
--- a/packages/PEGTL/src/example/pegtl/json_classes.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_classes.hpp
@@ -10,7 +10,7 @@
 #include <string>
 #include <vector>
 
-namespace examples
+namespace example
 {
    enum class json_type
    {
@@ -209,6 +209,6 @@ namespace examples
       }
    };
 
-}  // namespace examples
+}  // namespace example
 
 #endif
diff --git a/packages/PEGTL/src/example/pegtl/json_count.cpp b/packages/PEGTL/src/example/pegtl/json_count.cpp
index e0fb731afaf1c260f367d49dbc8adcb8b35a80a0..ee4bb71c0b22b8e51dcfd07d74fac05cedb9df1c 100644
--- a/packages/PEGTL/src/example/pegtl/json_count.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_count.cpp
@@ -32,19 +32,19 @@ namespace TAO_PEGTL_NAMESPACE
       template< typename Input >
       static void start( const Input& /*unused*/, counter_state& ts )
       {
-         ++ts.counts[ internal::demangle< Rule >() ].start;
+         ++ts.counts[ demangle< Rule >() ].start;
       }
 
       template< typename Input >
       static void success( const Input& /*unused*/, counter_state& ts )
       {
-         ++ts.counts[ internal::demangle< Rule >() ].success;
+         ++ts.counts[ demangle< Rule >() ].success;
       }
 
       template< typename Input >
       static void failure( const Input& /*unused*/, counter_state& ts )
       {
-         ++ts.counts[ internal::demangle< Rule >() ].failure;
+         ++ts.counts[ demangle< Rule >() ].failure;
       }
    };
 
diff --git a/packages/PEGTL/src/example/pegtl/json_coverage.cpp b/packages/PEGTL/src/example/pegtl/json_coverage.cpp
index b19c496929c3f5ce0cb3ad07c00599ed1b1b8c60..3d0ff1657d28288f8d8e457efa5db7b7e46bd375 100644
--- a/packages/PEGTL/src/example/pegtl/json_coverage.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_coverage.cpp
@@ -1,19 +1,45 @@
 // Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
+#include <iomanip>
 #include <iostream>
 
 #include <tao/pegtl.hpp>
-
 #include <tao/pegtl/contrib/coverage.hpp>
 #include <tao/pegtl/contrib/json.hpp>
 #include <tao/pegtl/contrib/print_coverage.hpp>
 
+#include "json_errors.hpp"
+
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+namespace example
+{
+   using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+}  // namespace example
+
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
-   for( int i = 1; i < argc; ++i ) {
-      auto coverage = tao::pegtl::coverage< tao::pegtl::json::text >( tao::pegtl::file_input( argv[ i ] ) );
-      tao::pegtl::print_coverage( std::cout, coverage );
+   if( argc != 2 ) {
+      std::cerr << "Usage: " << argv[ 0 ] << " FILE\n"
+                << "Print coverage of parsing FILE as JSON." << std::endl;
+      return 1;
+   }
+
+   pegtl::file_input in( argv[ 1 ] );
+   try {
+      pegtl::coverage_result result;
+      pegtl::coverage< example::grammar, pegtl::nothing, example::control >( in, result );  // Ignore return value...?
+      std::cout << result;
    }
+   catch( const pegtl::parse_error& e ) {
+      const auto p = e.positions().front();
+      std::cerr << e.what() << '\n'
+                << in.line_at( p ) << '\n'
+                << std::setw( p.column ) << '^' << std::endl;
+      return 1;
+   }
+
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/json_errors.hpp b/packages/PEGTL/src/example/pegtl/json_errors.hpp
index 4f674f805188bfd015944c3dcd30f325e4e2a8d7..847ae6fa5e4f534ea1dbda2227224a0b4c66b8ca 100644
--- a/packages/PEGTL/src/example/pegtl/json_errors.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_errors.hpp
@@ -9,7 +9,7 @@
 
 namespace pegtl = TAO_PEGTL_NAMESPACE;
 
-namespace examples
+namespace example
 {
    // This file shows how to throw exceptions with
    // custom error messages for parse errors.
@@ -38,12 +38,16 @@ namespace examples
 
    template<> inline constexpr auto error_message< pegtl::eof > = "unexpected character after JSON value";
 
-   // As must_if can not take error_message as a template parameter directly, we need to wrap it:
-   struct error { template< typename Rule > static constexpr auto message = error_message< Rule >; };
+   // As must_if<> can not take error_message as a template parameter directly, we need to wrap it.
+   // Additionally, we make sure only must<>-rules trigger global error.
+   struct error {
+      template< typename Rule > static constexpr auto raise_on_failure = false;
+      template< typename Rule > static constexpr auto message = error_message< Rule >;
+   };
 
    template< typename Rule > using control = pegtl::must_if< error >::control< Rule >;
    // clang-format on
 
-}  // namespace examples
+}  // namespace example
 
 #endif
diff --git a/packages/PEGTL/src/example/pegtl/json_parse.cpp b/packages/PEGTL/src/example/pegtl/json_parse.cpp
index 7db715bcc5314983cda4d7808c0bad1f6047e6b5..53cbd81e4b23d45c5bb1233d5afa76e1cbe43e7b 100644
--- a/packages/PEGTL/src/example/pegtl/json_parse.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_parse.cpp
@@ -1,18 +1,43 @@
 // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
+#include <iomanip>
+#include <iostream>
+
 #include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/json.hpp>
+#include <tao/pegtl/contrib/trace.hpp>
 
 #include "json_errors.hpp"
 
-using namespace TAO_PEGTL_NAMESPACE;
-using grammar = must< json::text, eof >;
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+namespace example
+{
+   using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+}  // namespace example
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
-   for( int i = 1; i < argc; ++i ) {
-      argv_input in( argv, i );
-      parse< grammar, nothing, examples::control >( in );
+   if( argc != 2 ) {
+      std::cerr << "Usage: " << argv[ 0 ] << " JSON\n"
+                << "Parse a JSON text.\n\n"
+                << "Example: " << argv[ 0 ] << " '{\"foo\":[42,null]}'" << std::endl;
+      return 1;
    }
+
+   pegtl::argv_input in( argv, 1 );
+   try {
+      pegtl::parse< example::grammar, pegtl::nothing, example::control >( in );
+   }
+   catch( const pegtl::parse_error& e ) {
+      const auto p = e.positions().front();
+      std::cerr << e.what() << '\n'
+                << in.line_at( p ) << '\n'
+                << std::setw( p.column ) << '^' << std::endl;
+      return 1;
+   }
+
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/json_print_rules.cpp b/packages/PEGTL/src/example/pegtl/json_print_debug.cpp
similarity index 82%
rename from packages/PEGTL/src/example/pegtl/json_print_rules.cpp
rename to packages/PEGTL/src/example/pegtl/json_print_debug.cpp
index 87850508fe761c236314a356b174f8d617a54146..9815d2c1e0fb29446e5321e74c2e0488c04d1b92 100644
--- a/packages/PEGTL/src/example/pegtl/json_print_rules.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_print_debug.cpp
@@ -8,6 +8,6 @@
 
 int main()  // NOLINT(bugprone-exception-escape)
 {
-   tao::pegtl::print_rules< tao::pegtl::json::text >( std::cout );
+   tao::pegtl::print_debug< tao::pegtl::json::text >( std::cout );
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/json_print_sub_rules.cpp b/packages/PEGTL/src/example/pegtl/json_print_names.cpp
similarity index 80%
rename from packages/PEGTL/src/example/pegtl/json_print_sub_rules.cpp
rename to packages/PEGTL/src/example/pegtl/json_print_names.cpp
index e28a04ef9fd3cd6ecd1fb0ca211be35401749f71..9f97008cf0469dab69603598de2b670a3bfb0ef5 100644
--- a/packages/PEGTL/src/example/pegtl/json_print_sub_rules.cpp
+++ b/packages/PEGTL/src/example/pegtl/json_print_names.cpp
@@ -8,6 +8,6 @@
 
 int main()  // NOLINT(bugprone-exception-escape)
 {
-   tao::pegtl::print_sub_rules< tao::pegtl::json::text >( std::cout );
+   tao::pegtl::print_names< tao::pegtl::json::text >( std::cout );
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/json_trace.cpp b/packages/PEGTL/src/example/pegtl/json_trace.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5cba1349f8cbe12123a6dc237aff6f392f15ba6a
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/json_trace.cpp
@@ -0,0 +1,43 @@
+// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iomanip>
+#include <iostream>
+
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/json.hpp>
+#include <tao/pegtl/contrib/trace.hpp>
+
+#include "json_errors.hpp"
+
+namespace pegtl = TAO_PEGTL_NAMESPACE;
+
+namespace example
+{
+   using grammar = pegtl::must< pegtl::json::text, pegtl::eof >;
+
+}  // namespace example
+
+int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
+{
+   if( argc != 2 ) {
+      std::cerr << "Usage: " << argv[ 0 ] << " JSON\n"
+                << "Trace parsing a JSON text.\n\n"
+                << "Example: " << argv[ 0 ] << " '{\"foo\":[42,null]}'" << std::endl;
+      return 1;
+   }
+
+   pegtl::argv_input in( argv, 1 );
+   try {
+      pegtl::standard_trace< example::grammar, pegtl::nothing, example::control >( in );
+   }
+   catch( const pegtl::parse_error& e ) {
+      const auto p = e.positions().front();
+      std::cerr << e.what() << '\n'
+                << in.line_at( p ) << '\n'
+                << std::setw( p.column ) << '^' << std::endl;
+      return 1;
+   }
+
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/json_unescape.hpp b/packages/PEGTL/src/example/pegtl/json_unescape.hpp
index 974c6afb77f46aa04614f8fb629c45fd41b4175d..60e30c7beff0eddadd0077f0acb6171e002203ce 100644
--- a/packages/PEGTL/src/example/pegtl/json_unescape.hpp
+++ b/packages/PEGTL/src/example/pegtl/json_unescape.hpp
@@ -10,7 +10,7 @@
 #include <tao/pegtl/contrib/json.hpp>
 #include <tao/pegtl/contrib/unescape.hpp>
 
-namespace examples
+namespace example
 {
    // Action class for parsing literal strings, uses the PEGTL unescape utilities, cf. unescape.cpp.
 
@@ -24,6 +24,6 @@ namespace examples
 
    using json_unescape = tao::pegtl::change_action_and_states< json_unescape_action, std::string >;
 
-}  // namespace examples
+}  // namespace example
 
 #endif
diff --git a/packages/PEGTL/src/example/pegtl/lua53.hpp b/packages/PEGTL/src/example/pegtl/lua53.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5f040509029fb3e8ef40d450032f92dad2af7dff
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/lua53.hpp
@@ -0,0 +1,335 @@
+// Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#ifndef TAO_PEGTL_SRC_EXAMPLES_PEGTL_LUA53_HPP
+#define TAO_PEGTL_SRC_EXAMPLES_PEGTL_LUA53_HPP
+
+#include <tao/pegtl.hpp>
+#include <tao/pegtl/contrib/raw_string.hpp>
+
+namespace lua53
+{
+   // PEGTL grammar for the Lua 5.3.0 lexer and parser.
+   //
+   // The grammar here is not very similar to the grammar
+   // in the Lua reference documentation on which it is based
+   // which is due to multiple causes.
+   //
+   // The main difference is that this grammar includes really
+   // "everything", not just the structural parts from the
+   // reference documentation:
+   // - The PEG-approach combines lexer and parser; this grammar
+   //   handles comments and tokenisation.
+   // - The operator precedence and associativity are reflected
+   //   in the structure of this grammar.
+   // - All details for all types of literals are included, with
+   //   escape-sequences for literal strings, and long literals.
+   //
+   // The second necessary difference is that all left-recursion
+   // had to be eliminated.
+   //
+   // In some places the grammar was optimised to require as little
+   // back-tracking as possible, most prominently for expressions.
+   // The original grammar contains the following production rules:
+   //
+   //   prefixexp ::= var | functioncall | ‘(’ exp ‘)’
+   //   functioncall ::=  prefixexp args | prefixexp ‘:’ Name args
+   //   var ::=  Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name
+   //
+   // We need to eliminate the left-recursion, and we also want to
+   // remove the ambiguity between function calls and variables,
+   // i.e. the fact that we can have expressions like
+   //
+   //   ( a * b ).c()[ d ].e:f()
+   //
+   // where only the last element decides between function call and
+   // variable, making it necessary to parse the whole thing again
+   // if we chose wrong at the beginning.
+   // First we eliminate prefixexp and obtain:
+   //
+   //   functioncall ::=  ( var | functioncall | ‘(’ exp ‘)’ ) ( args | ‘:’ Name args )
+   //   var ::=  Name | ( var | functioncall | ‘(’ exp ‘)’ ) ( ‘[’ exp ‘]’ | ‘.’ Name )
+   //
+   // Next we split function_call and variable into a first part,
+   // a "head", or how they can start, and a second part, the "tail",
+   // which, in a sequence like above, is the final deciding part:
+   //
+   //   vartail ::= '[' exp ']' | '.' Name
+   //   varhead ::= Name | '(' exp ')' vartail
+   //   functail ::= args | ':' Name args
+   //   funchead ::= Name | '(' exp ')'
+   //
+   // This allows us to rewrite var and function_call as follows.
+   //
+   //   var ::= varhead { { functail } vartail }
+   //   function_call ::= funchead [ { vartail } functail ]
+   //
+   // Finally we can define a single expression that takes care
+   // of var, function_call, and expressions in a bracket:
+   //
+   //   chead ::= '(' exp ')' | Name
+   //   combined ::= chead { functail | vartail }
+   //
+   // Such a combined expression starts with a bracketed
+   // expression or a name, and continues with an arbitrary
+   // number of functail and/or vartail parts, all in a one
+   // grammar rule without back-tracking.
+   //
+   // The rule expr_thirteen below implements "combined".
+   //
+   // Another issue of interest when writing a PEG is how to
+   // manage the separators, the white-space and comments that
+   // can occur in many places; in the classical two-stage
+   // lexer-parser approach the lexer would have taken care of
+   // this, but here we use the PEG approach that combines both.
+   //
+   // In the following grammar most rules adopt the convention
+   // that they take care of "internal padding", i.e. spaces
+   // and comments that can occur within the rule, but not
+   // "external padding", i.e. they don't start or end with
+   // a rule that "eats up" all extra padding (spaces and
+   // comments). In some places, where it is more efficient,
+   // right padding is used.
+
+   // clang-format off
+   struct short_comment : tao::pegtl::until< tao::pegtl::eolf > {};
+   struct long_string : tao::pegtl::raw_string< '[', '=', ']' > {};
+   struct comment : tao::pegtl::disable< tao::pegtl::two< '-' >, tao::pegtl::sor< long_string, short_comment > > {};
+
+   struct sep : tao::pegtl::sor< tao::pegtl::ascii::space, comment > {};
+   struct seps : tao::pegtl::star< sep > {};
+
+   struct str_and : TAO_PEGTL_STRING( "and" ) {};
+   struct str_break : TAO_PEGTL_STRING( "break" ) {};
+   struct str_do : TAO_PEGTL_STRING( "do" ) {};
+   struct str_else : TAO_PEGTL_STRING( "else" ) {};
+   struct str_elseif : TAO_PEGTL_STRING( "elseif" ) {};
+   struct str_end : TAO_PEGTL_STRING( "end" ) {};
+   struct str_false : TAO_PEGTL_STRING( "false" ) {};
+   struct str_for : TAO_PEGTL_STRING( "for" ) {};
+   struct str_function : TAO_PEGTL_STRING( "function" ) {};
+   struct str_goto : TAO_PEGTL_STRING( "goto" ) {};
+   struct str_if : TAO_PEGTL_STRING( "if" ) {};
+   struct str_in : TAO_PEGTL_STRING( "in" ) {};
+   struct str_local : TAO_PEGTL_STRING( "local" ) {};
+   struct str_nil : TAO_PEGTL_STRING( "nil" ) {};
+   struct str_not : TAO_PEGTL_STRING( "not" ) {};
+   struct str_or : TAO_PEGTL_STRING( "or" ) {};
+   struct str_repeat : TAO_PEGTL_STRING( "repeat" ) {};
+   struct str_return : TAO_PEGTL_STRING( "return" ) {};
+   struct str_then : TAO_PEGTL_STRING( "then" ) {};
+   struct str_true : TAO_PEGTL_STRING( "true" ) {};
+   struct str_until : TAO_PEGTL_STRING( "until" ) {};
+   struct str_while : TAO_PEGTL_STRING( "while" ) {};
+
+   // Note that 'elseif' precedes 'else' in order to prevent only matching
+   // the "else" part of an "elseif" and running into an error in the
+   // 'keyword' rule.
+
+   template< typename Key >
+   struct key : tao::pegtl::seq< Key, tao::pegtl::not_at< tao::pegtl::identifier_other > > {};
+
+   struct sor_keyword : tao::pegtl::sor< str_and, str_break, str_do, str_elseif, str_else, str_end, str_false, str_for, str_function, str_goto, str_if, str_in, str_local, str_nil, str_not, str_repeat, str_return, str_then, str_true, str_until, str_while > {};
+
+   struct key_and : key< str_and > {};
+   struct key_break : key< str_break > {};
+   struct key_do : key< str_do > {};
+   struct key_else : key< str_else > {};
+   struct key_elseif : key< str_elseif > {};
+   struct key_end : key< str_end > {};
+   struct key_false : key< str_false > {};
+   struct key_for : key< str_for > {};
+   struct key_function : key< str_function > {};
+   struct key_goto : key< str_goto > {};
+   struct key_if : key< str_if > {};
+   struct key_in : key< str_in > {};
+   struct key_local : key< str_local > {};
+   struct key_nil : key< str_nil > {};
+   struct key_not : key< str_not > {};
+   struct key_or : key< str_or > {};
+   struct key_repeat : key< str_repeat > {};
+   struct key_return : key< str_return > {};
+   struct key_then : key< str_then > {};
+   struct key_true : key< str_true > {};
+   struct key_until : key< str_until > {};
+   struct key_while : key< str_while > {};
+
+   struct keyword : key< sor_keyword > {};
+
+   template< typename R >
+   struct pad : tao::pegtl::pad< R, sep > {};
+
+   struct name : tao::pegtl::seq< tao::pegtl::not_at< keyword >, tao::pegtl::identifier > {};
+
+   struct single : tao::pegtl::one< 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0', '\n' > {};
+   struct spaces : tao::pegtl::seq< tao::pegtl::one< 'z' >, tao::pegtl::star< tao::pegtl::space > > {};
+   struct hexbyte : tao::pegtl::if_must< tao::pegtl::one< 'x' >, tao::pegtl::xdigit, tao::pegtl::xdigit > {};
+   struct decbyte : tao::pegtl::if_must< tao::pegtl::digit, tao::pegtl::rep_opt< 2, tao::pegtl::digit > > {};
+   struct unichar : tao::pegtl::if_must< tao::pegtl::one< 'u' >, tao::pegtl::one< '{' >, tao::pegtl::plus< tao::pegtl::xdigit >, tao::pegtl::one< '}' > > {};
+   struct escaped : tao::pegtl::if_must< tao::pegtl::one< '\\' >, tao::pegtl::sor< hexbyte, decbyte, unichar, single, spaces > > {};
+   struct regular : tao::pegtl::not_one< '\r', '\n' > {};
+   struct character : tao::pegtl::sor< escaped, regular > {};
+
+   template< char Q >
+   struct short_string : tao::pegtl::if_must< tao::pegtl::one< Q >, tao::pegtl::until< tao::pegtl::one< Q >, character > > {};
+   struct literal_string : tao::pegtl::sor< short_string< '"' >, short_string< '\'' >, long_string > {};
+
+   template< typename E >
+   struct exponent : tao::pegtl::opt_must< E, tao::pegtl::opt< tao::pegtl::one< '+', '-' > >, tao::pegtl::plus< tao::pegtl::digit > > {};
+
+   template< typename D, typename E >
+   struct numeral_three : tao::pegtl::seq< tao::pegtl::if_must< tao::pegtl::one< '.' >, tao::pegtl::plus< D > >, exponent< E > > {};
+   template< typename D, typename E >
+   struct numeral_two : tao::pegtl::seq< tao::pegtl::plus< D >, tao::pegtl::opt< tao::pegtl::one< '.' >, tao::pegtl::star< D > >, exponent< E > > {};
+   template< typename D, typename E >
+   struct numeral_one : tao::pegtl::sor< numeral_two< D, E >, numeral_three< D, E > > {};
+
+   struct decimal : numeral_one< tao::pegtl::digit, tao::pegtl::one< 'e', 'E' > > {};
+   struct hexadecimal : tao::pegtl::if_must< tao::pegtl::istring< '0', 'x' >, numeral_one< tao::pegtl::xdigit, tao::pegtl::one< 'p', 'P' > > > {};
+   struct numeral : tao::pegtl::sor< hexadecimal, decimal > {};
+
+   struct label_statement : tao::pegtl::if_must< tao::pegtl::two< ':' >, seps, name, seps, tao::pegtl::two< ':' > > {};
+   struct goto_statement : tao::pegtl::if_must< key_goto, seps, name > {};
+
+   struct statement;
+   struct expression;
+
+   struct name_list : tao::pegtl::list< name, tao::pegtl::one< ',' >, sep > {};
+   struct name_list_must : tao::pegtl::list_must< name, tao::pegtl::one< ',' >, sep > {};
+   struct expr_list_must : tao::pegtl::list_must< expression, tao::pegtl::one< ',' >, sep > {};
+
+   struct statement_return : tao::pegtl::seq< tao::pegtl::pad_opt< expr_list_must, sep >, tao::pegtl::opt< tao::pegtl::one< ';' >, seps > > {};
+
+   template< typename E >
+   struct statement_list : tao::pegtl::seq< seps, tao::pegtl::until< tao::pegtl::sor< E, tao::pegtl::if_must< key_return, statement_return, E > >, statement, seps > > {};
+
+   template< char O, char... N >
+   struct op_one : tao::pegtl::seq< tao::pegtl::one< O >, tao::pegtl::at< tao::pegtl::not_one< N... > > > {};
+   template< char O, char P, char... N >
+   struct op_two : tao::pegtl::seq< tao::pegtl::string< O, P >, tao::pegtl::at< tao::pegtl::not_one< N... > > > {};
+
+   struct table_field_one : tao::pegtl::if_must< tao::pegtl::one< '[' >, seps, expression, seps, tao::pegtl::one< ']' >, seps, tao::pegtl::one< '=' >, seps, expression > {};
+   struct table_field_two : tao::pegtl::if_must< tao::pegtl::seq< name, seps, op_one< '=', '=' > >, seps, expression > {};
+   struct table_field : tao::pegtl::sor< table_field_one, table_field_two, expression > {};
+   struct table_field_list : tao::pegtl::list_tail< table_field, tao::pegtl::one< ',', ';' >, sep > {};
+   struct table_constructor : tao::pegtl::if_must< tao::pegtl::one< '{' >, tao::pegtl::pad_opt< table_field_list, sep >, tao::pegtl::one< '}' > > {};
+
+   struct parameter_list_one : tao::pegtl::seq< name_list, tao::pegtl::opt_must< pad< tao::pegtl::one< ',' > >, tao::pegtl::ellipsis > > {};
+   struct parameter_list : tao::pegtl::sor< tao::pegtl::ellipsis, parameter_list_one > {};
+
+   struct function_body : tao::pegtl::seq< tao::pegtl::one< '(' >, tao::pegtl::pad_opt< parameter_list, sep >, tao::pegtl::one< ')' >, seps, statement_list< key_end > > {};
+   struct function_literal : tao::pegtl::if_must< key_function, seps, function_body > {};
+
+   struct bracket_expr : tao::pegtl::if_must< tao::pegtl::one< '(' >, seps, expression, seps, tao::pegtl::one< ')' > > {};
+
+   struct function_args_one : tao::pegtl::if_must< tao::pegtl::one< '(' >, tao::pegtl::pad_opt< expr_list_must, sep >, tao::pegtl::one< ')' > > {};
+   struct function_args : tao::pegtl::sor< function_args_one, table_constructor, literal_string > {};
+
+   struct variable_tail_one : tao::pegtl::if_must< tao::pegtl::one< '[' >, seps, expression, seps, tao::pegtl::one< ']' > > {};
+   struct variable_tail_two : tao::pegtl::if_must< tao::pegtl::seq< tao::pegtl::not_at< tao::pegtl::two< '.' > >, tao::pegtl::one< '.' > >, seps, name > {};
+   struct variable_tail : tao::pegtl::sor< variable_tail_one, variable_tail_two > {};
+
+   struct function_call_tail_one : tao::pegtl::if_must< tao::pegtl::seq< tao::pegtl::not_at< tao::pegtl::two< ':' > >, tao::pegtl::one< ':' > >, seps, name, seps, function_args > {};
+   struct function_call_tail : tao::pegtl::sor< function_args, function_call_tail_one > {};
+
+   struct variable_head_one : tao::pegtl::seq< bracket_expr, seps, variable_tail > {};
+   struct variable_head : tao::pegtl::sor< name, variable_head_one > {};
+
+   struct function_call_head : tao::pegtl::sor< name, bracket_expr > {};
+
+   struct variable : tao::pegtl::seq< variable_head, tao::pegtl::star< tao::pegtl::star< seps, function_call_tail >, seps, variable_tail > > {};
+   struct function_call : tao::pegtl::seq< function_call_head, tao::pegtl::plus< tao::pegtl::until< tao::pegtl::seq< seps, function_call_tail >, seps, variable_tail > > > {};
+
+   template< typename S, typename O >
+   struct left_assoc : tao::pegtl::seq< S, seps, tao::pegtl::star_must< O, seps, S, seps > > {};
+   template< typename S, typename O >
+   struct right_assoc : tao::pegtl::seq< S, seps, tao::pegtl::opt_must< O, seps, right_assoc< S, O > > > {};
+
+   struct unary_operators : tao::pegtl::sor< tao::pegtl::one< '-' >,
+                                             tao::pegtl::one< '#' >,
+                                             op_one< '~', '=' >,
+                                             key_not > {};
+
+   struct expr_ten;
+   struct expr_thirteen : tao::pegtl::seq< tao::pegtl::sor< bracket_expr, name >, tao::pegtl::star< seps, tao::pegtl::sor< function_call_tail, variable_tail > > > {};
+   struct expr_twelve : tao::pegtl::sor< key_nil,
+                                         key_true,
+                                         key_false,
+                                         tao::pegtl::ellipsis,
+                                         numeral,
+                                         literal_string,
+                                         function_literal,
+                                         expr_thirteen,
+                                         table_constructor > {};
+   struct expr_eleven : tao::pegtl::seq< expr_twelve, seps, tao::pegtl::opt< tao::pegtl::one< '^' >, seps, expr_ten, seps > > {};
+   struct unary_apply : tao::pegtl::if_must< unary_operators, seps, expr_ten, seps > {};
+   struct expr_ten : tao::pegtl::sor< unary_apply, expr_eleven > {};
+   struct operators_nine : tao::pegtl::sor< tao::pegtl::two< '/' >,
+                                            tao::pegtl::one< '/' >,
+                                            tao::pegtl::one< '*' >,
+                                            tao::pegtl::one< '%' > > {};
+   struct expr_nine : left_assoc< expr_ten, operators_nine > {};
+   struct operators_eight : tao::pegtl::sor< tao::pegtl::one< '+' >,
+                                             tao::pegtl::one< '-' > > {};
+   struct expr_eight : left_assoc< expr_nine, operators_eight > {};
+   struct expr_seven : right_assoc< expr_eight, op_two< '.', '.', '.' > > {};
+   struct operators_six : tao::pegtl::sor< tao::pegtl::two< '<' >,
+                                           tao::pegtl::two< '>' > > {};
+   struct expr_six : left_assoc< expr_seven, operators_six > {};
+   struct expr_five : left_assoc< expr_six, tao::pegtl::one< '&' > > {};
+   struct expr_four : left_assoc< expr_five, op_one< '~', '=' > > {};
+   struct expr_three : left_assoc< expr_four, tao::pegtl::one< '|' > > {};
+   struct operators_two : tao::pegtl::sor< tao::pegtl::two< '=' >,
+                                           tao::pegtl::string< '<', '=' >,
+                                           tao::pegtl::string< '>', '=' >,
+                                           op_one< '<', '<' >,
+                                           op_one< '>', '>' >,
+                                           tao::pegtl::string< '~', '=' > > {};
+   struct expr_two : left_assoc< expr_three, operators_two > {};
+   struct expr_one : left_assoc< expr_two, key_and > {};
+   struct expression : left_assoc< expr_one, key_or > {};
+
+   struct do_statement : tao::pegtl::if_must< key_do, statement_list< key_end > > {};
+   struct while_statement : tao::pegtl::if_must< key_while, seps, expression, seps, key_do, statement_list< key_end > > {};
+   struct repeat_statement : tao::pegtl::if_must< key_repeat, statement_list< key_until >, seps, expression > {};
+
+   struct at_elseif_else_end : tao::pegtl::sor< tao::pegtl::at< key_elseif >, tao::pegtl::at< key_else >, tao::pegtl::at< key_end > > {};
+   struct elseif_statement : tao::pegtl::if_must< key_elseif, seps, expression, seps, key_then, statement_list< at_elseif_else_end > > {};
+   struct else_statement : tao::pegtl::if_must< key_else, statement_list< key_end > > {};
+   struct if_statement : tao::pegtl::if_must< key_if, seps, expression, seps, key_then, statement_list< at_elseif_else_end >, seps, tao::pegtl::until< tao::pegtl::sor< else_statement, key_end >, elseif_statement, seps > > {};
+
+   struct for_statement_one : tao::pegtl::seq< tao::pegtl::one< '=' >, seps, expression, seps, tao::pegtl::one< ',' >, seps, expression, tao::pegtl::pad_opt< tao::pegtl::if_must< tao::pegtl::one< ',' >, seps, expression >, sep > > {};
+   struct for_statement_two : tao::pegtl::seq< tao::pegtl::opt_must< tao::pegtl::one< ',' >, seps, name_list_must, seps >, key_in, seps, expr_list_must, seps > {};
+   struct for_statement : tao::pegtl::if_must< key_for, seps, name, seps, tao::pegtl::sor< for_statement_one, for_statement_two >, key_do, statement_list< key_end > > {};
+
+   struct assignment_variable_list : tao::pegtl::list_must< variable, tao::pegtl::one< ',' >, sep > {};
+   struct assignments_one : tao::pegtl::if_must< tao::pegtl::one< '=' >, seps, expr_list_must > {};
+   struct assignments : tao::pegtl::seq< assignment_variable_list, seps, assignments_one > {};
+   struct function_name : tao::pegtl::seq< tao::pegtl::list< name, tao::pegtl::one< '.' >, sep >, seps, tao::pegtl::opt_must< tao::pegtl::one< ':' >, seps, name, seps > > {};
+   struct function_definition : tao::pegtl::if_must< key_function, seps, function_name, function_body > {};
+
+   struct local_function : tao::pegtl::if_must< key_function, seps, name, seps, function_body > {};
+   struct local_variables : tao::pegtl::if_must< name_list_must, seps, tao::pegtl::opt< assignments_one > > {};
+   struct local_statement : tao::pegtl::if_must< key_local, seps, tao::pegtl::sor< local_function, local_variables > > {};
+
+   struct semicolon : tao::pegtl::one< ';' > {};
+   struct statement : tao::pegtl::sor< semicolon,
+                                       assignments,
+                                       function_call,
+                                       label_statement,
+                                       key_break,
+                                       goto_statement,
+                                       do_statement,
+                                       while_statement,
+                                       repeat_statement,
+                                       if_statement,
+                                       for_statement,
+                                       function_definition,
+                                       local_statement > {};
+
+   struct grammar : tao::pegtl::must< tao::pegtl::opt< tao::pegtl::shebang >, statement_list< tao::pegtl::eof > > {};
+   // clang-format on
+
+}  // namespace lua53
+
+#endif
diff --git a/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp b/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f6d6c847e7c576726bc6e86e6a7effd8929f205a
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/lua53_analyze.cpp
@@ -0,0 +1,17 @@
+// Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl/contrib/analyze.hpp>
+
+#include "lua53.hpp"
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   if( const auto problems = TAO_PEGTL_NAMESPACE::analyze< lua53::grammar >() != 0 ) {
+      std::cout << "problems: " << problems << std::endl;
+      return 1;
+   }
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/lua53_parse.cpp b/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
index ddb1df06b2c75ac04874ec1fc074e49a0cb81469..6b84aff24753be1b2027d050b7516e390badf282 100644
--- a/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
+++ b/packages/PEGTL/src/example/pegtl/lua53_parse.cpp
@@ -1,347 +1,16 @@
 // Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
-#include <tao/pegtl.hpp>
-#include <tao/pegtl/contrib/analyze.hpp>
-#include <tao/pegtl/contrib/raw_string.hpp>
+#include <iostream>
 
-namespace lua53
-{
-   // PEGTL grammar for the Lua 5.3.0 lexer and parser.
-   //
-   // The grammar here is not very similar to the grammar
-   // in the Lua reference documentation on which it is based
-   // which is due to multiple causes.
-   //
-   // The main difference is that this grammar includes really
-   // "everything", not just the structural parts from the
-   // reference documentation:
-   // - The PEG-approach combines lexer and parser; this grammar
-   //   handles comments and tokenisation.
-   // - The operator precedence and associativity are reflected
-   //   in the structure of this grammar.
-   // - All details for all types of literals are included, with
-   //   escape-sequences for literal strings, and long literals.
-   //
-   // The second necessary difference is that all left-recursion
-   // had to be eliminated.
-   //
-   // In some places the grammar was optimised to require as little
-   // back-tracking as possible, most prominently for expressions.
-   // The original grammar contains the following production rules:
-   //
-   //   prefixexp ::= var | functioncall | ‘(’ exp ‘)’
-   //   functioncall ::=  prefixexp args | prefixexp ‘:’ Name args
-   //   var ::=  Name | prefixexp ‘[’ exp ‘]’ | prefixexp ‘.’ Name
-   //
-   // We need to eliminate the left-recursion, and we also want to
-   // remove the ambiguity between function calls and variables,
-   // i.e. the fact that we can have expressions like
-   //
-   //   ( a * b ).c()[ d ].e:f()
-   //
-   // where only the last element decides between function call and
-   // variable, making it necessary to parse the whole thing again
-   // if we chose wrong at the beginning.
-   // First we eliminate prefixexp and obtain:
-   //
-   //   functioncall ::=  ( var | functioncall | ‘(’ exp ‘)’ ) ( args | ‘:’ Name args )
-   //   var ::=  Name | ( var | functioncall | ‘(’ exp ‘)’ ) ( ‘[’ exp ‘]’ | ‘.’ Name )
-   //
-   // Next we split function_call and variable into a first part,
-   // a "head", or how they can start, and a second part, the "tail",
-   // which, in a sequence like above, is the final deciding part:
-   //
-   //   vartail ::= '[' exp ']' | '.' Name
-   //   varhead ::= Name | '(' exp ')' vartail
-   //   functail ::= args | ':' Name args
-   //   funchead ::= Name | '(' exp ')'
-   //
-   // This allows us to rewrite var and function_call as follows.
-   //
-   //   var ::= varhead { { functail } vartail }
-   //   function_call ::= funchead [ { vartail } functail ]
-   //
-   // Finally we can define a single expression that takes care
-   // of var, function_call, and expressions in a bracket:
-   //
-   //   chead ::= '(' exp ')' | Name
-   //   combined ::= chead { functail | vartail }
-   //
-   // Such a combined expression starts with a bracketed
-   // expression or a name, and continues with an arbitrary
-   // number of functail and/or vartail parts, all in a one
-   // grammar rule without back-tracking.
-   //
-   // The rule expr_thirteen below implements "combined".
-   //
-   // Another issue of interest when writing a PEG is how to
-   // manage the separators, the white-space and comments that
-   // can occur in many places; in the classical two-stage
-   // lexer-parser approach the lexer would have taken care of
-   // this, but here we use the PEG approach that combines both.
-   //
-   // In the following grammar most rules adopt the convention
-   // that they take care of "internal padding", i.e. spaces
-   // and comments that can occur within the rule, but not
-   // "external padding", i.e. they don't start or end with
-   // a rule that "eats up" all extra padding (spaces and
-   // comments). In some places, where it is more efficient,
-   // right padding is used.
-
-   namespace pegtl = TAO_PEGTL_NAMESPACE;
-
-   // clang-format off
-   struct short_comment : pegtl::until< pegtl::eolf > {};
-   struct long_string : pegtl::raw_string< '[', '=', ']' > {};
-   struct comment : pegtl::disable< pegtl::two< '-' >, pegtl::sor< long_string, short_comment > > {};
-
-   struct sep : pegtl::sor< pegtl::ascii::space, comment > {};
-   struct seps : pegtl::star< sep > {};
-
-   struct str_and : TAO_PEGTL_STRING( "and" ) {};
-   struct str_break : TAO_PEGTL_STRING( "break" ) {};
-   struct str_do : TAO_PEGTL_STRING( "do" ) {};
-   struct str_else : TAO_PEGTL_STRING( "else" ) {};
-   struct str_elseif : TAO_PEGTL_STRING( "elseif" ) {};
-   struct str_end : TAO_PEGTL_STRING( "end" ) {};
-   struct str_false : TAO_PEGTL_STRING( "false" ) {};
-   struct str_for : TAO_PEGTL_STRING( "for" ) {};
-   struct str_function : TAO_PEGTL_STRING( "function" ) {};
-   struct str_goto : TAO_PEGTL_STRING( "goto" ) {};
-   struct str_if : TAO_PEGTL_STRING( "if" ) {};
-   struct str_in : TAO_PEGTL_STRING( "in" ) {};
-   struct str_local : TAO_PEGTL_STRING( "local" ) {};
-   struct str_nil : TAO_PEGTL_STRING( "nil" ) {};
-   struct str_not : TAO_PEGTL_STRING( "not" ) {};
-   struct str_or : TAO_PEGTL_STRING( "or" ) {};
-   struct str_repeat : TAO_PEGTL_STRING( "repeat" ) {};
-   struct str_return : TAO_PEGTL_STRING( "return" ) {};
-   struct str_then : TAO_PEGTL_STRING( "then" ) {};
-   struct str_true : TAO_PEGTL_STRING( "true" ) {};
-   struct str_until : TAO_PEGTL_STRING( "until" ) {};
-   struct str_while : TAO_PEGTL_STRING( "while" ) {};
-
-   // Note that 'elseif' precedes 'else' in order to prevent only matching
-   // the "else" part of an "elseif" and running into an error in the
-   // 'keyword' rule.
-
-   struct str_keyword : pegtl::sor< str_and, str_break, str_do, str_elseif, str_else, str_end, str_false, str_for, str_function, str_goto, str_if, str_in, str_local, str_nil, str_not, str_repeat, str_return, str_then, str_true, str_until, str_while > {};
-
-   template< typename Key >
-   struct key : pegtl::seq< Key, pegtl::not_at< pegtl::identifier_other > > {};
-
-   struct key_and : key< str_and > {};
-   struct key_break : key< str_break > {};
-   struct key_do : key< str_do > {};
-   struct key_else : key< str_else > {};
-   struct key_elseif : key< str_elseif > {};
-   struct key_end : key< str_end > {};
-   struct key_false : key< str_false > {};
-   struct key_for : key< str_for > {};
-   struct key_function : key< str_function > {};
-   struct key_goto : key< str_goto > {};
-   struct key_if : key< str_if > {};
-   struct key_in : key< str_in > {};
-   struct key_local : key< str_local > {};
-   struct key_nil : key< str_nil > {};
-   struct key_not : key< str_not > {};
-   struct key_or : key< str_or > {};
-   struct key_repeat : key< str_repeat > {};
-   struct key_return : key< str_return > {};
-   struct key_then : key< str_then > {};
-   struct key_true : key< str_true > {};
-   struct key_until : key< str_until > {};
-   struct key_while : key< str_while > {};
-
-   struct keyword : key< str_keyword > {};
-
-   template< typename R >
-   struct pad : pegtl::pad< R, sep > {};
-
-   struct name : pegtl::seq< pegtl::not_at< keyword >, pegtl::identifier > {};
-
-   struct single : pegtl::one< 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"', '\'', '0', '\n' > {};
-   struct spaces : pegtl::seq< pegtl::one< 'z' >, pegtl::star< pegtl::space > > {};
-   struct hexbyte : pegtl::if_must< pegtl::one< 'x' >, pegtl::xdigit, pegtl::xdigit > {};
-   struct decbyte : pegtl::if_must< pegtl::digit, pegtl::rep_opt< 2, pegtl::digit > > {};
-   struct unichar : pegtl::if_must< pegtl::one< 'u' >, pegtl::one< '{' >, pegtl::plus< pegtl::xdigit >, pegtl::one< '}' > > {};
-   struct escaped : pegtl::if_must< pegtl::one< '\\' >, pegtl::sor< hexbyte, decbyte, unichar, single, spaces > > {};
-   struct regular : pegtl::not_one< '\r', '\n' > {};
-   struct character : pegtl::sor< escaped, regular > {};
-
-   template< char Q >
-   struct short_string : pegtl::if_must< pegtl::one< Q >, pegtl::until< pegtl::one< Q >, character > > {};
-   struct literal_string : pegtl::sor< short_string< '"' >, short_string< '\'' >, long_string > {};
-
-   template< typename E >
-   struct exponent : pegtl::opt_must< E, pegtl::opt< pegtl::one< '+', '-' > >, pegtl::plus< pegtl::digit > > {};
-
-   template< typename D, typename E >
-   struct numeral_three : pegtl::seq< pegtl::if_must< pegtl::one< '.' >, pegtl::plus< D > >, exponent< E > > {};
-   template< typename D, typename E >
-   struct numeral_two : pegtl::seq< pegtl::plus< D >, pegtl::opt< pegtl::one< '.' >, pegtl::star< D > >, exponent< E > > {};
-   template< typename D, typename E >
-   struct numeral_one : pegtl::sor< numeral_two< D, E >, numeral_three< D, E > > {};
-
-   struct decimal : numeral_one< pegtl::digit, pegtl::one< 'e', 'E' > > {};
-   struct hexadecimal : pegtl::if_must< pegtl::istring< '0', 'x' >, numeral_one< pegtl::xdigit, pegtl::one< 'p', 'P' > > > {};
-   struct numeral : pegtl::sor< hexadecimal, decimal > {};
-
-   struct label_statement : pegtl::if_must< pegtl::two< ':' >, seps, name, seps, pegtl::two< ':' > > {};
-   struct goto_statement : pegtl::if_must< key_goto, seps, name > {};
-
-   struct statement;
-   struct expression;
-
-   struct name_list : pegtl::list< name, pegtl::one< ',' >, sep > {};
-   struct name_list_must : pegtl::list_must< name, pegtl::one< ',' >, sep > {};
-   struct expr_list_must : pegtl::list_must< expression, pegtl::one< ',' >, sep > {};
-
-   struct statement_return : pegtl::seq< pegtl::pad_opt< expr_list_must, sep >, pegtl::opt< pegtl::one< ';' >, seps > > {};
-
-   template< typename E >
-   struct statement_list : pegtl::seq< seps, pegtl::until< pegtl::sor< E, pegtl::if_must< key_return, statement_return, E > >, statement, seps > > {};
-
-   template< char O, char... N >
-   struct op_one : pegtl::seq< pegtl::one< O >, pegtl::at< pegtl::not_one< N... > > > {};
-   template< char O, char P, char... N >
-   struct op_two : pegtl::seq< pegtl::string< O, P >, pegtl::at< pegtl::not_one< N... > > > {};
-
-   struct table_field_one : pegtl::if_must< pegtl::one< '[' >, seps, expression, seps, pegtl::one< ']' >, seps, pegtl::one< '=' >, seps, expression > {};
-   struct table_field_two : pegtl::if_must< pegtl::seq< name, seps, op_one< '=', '=' > >, seps, expression > {};
-   struct table_field : pegtl::sor< table_field_one, table_field_two, expression > {};
-   struct table_field_list : pegtl::list_tail< table_field, pegtl::one< ',', ';' >, sep > {};
-   struct table_constructor : pegtl::if_must< pegtl::one< '{' >, pegtl::pad_opt< table_field_list, sep >, pegtl::one< '}' > > {};
-
-   struct parameter_list_one : pegtl::seq< name_list, pegtl::opt_must< pad< pegtl::one< ',' > >, pegtl::ellipsis > > {};
-   struct parameter_list : pegtl::sor< pegtl::ellipsis, parameter_list_one > {};
-
-   struct function_body : pegtl::seq< pegtl::one< '(' >, pegtl::pad_opt< parameter_list, sep >, pegtl::one< ')' >, seps, statement_list< key_end > > {};
-   struct function_literal : pegtl::if_must< key_function, seps, function_body > {};
-
-   struct bracket_expr : pegtl::if_must< pegtl::one< '(' >, seps, expression, seps, pegtl::one< ')' > > {};
-
-   struct function_args_one : pegtl::if_must< pegtl::one< '(' >, pegtl::pad_opt< expr_list_must, sep >, pegtl::one< ')' > > {};
-   struct function_args : pegtl::sor< function_args_one, table_constructor, literal_string > {};
-
-   struct variable_tail_one : pegtl::if_must< pegtl::one< '[' >, seps, expression, seps, pegtl::one< ']' > > {};
-   struct variable_tail_two : pegtl::if_must< pegtl::seq< pegtl::not_at< pegtl::two< '.' > >, pegtl::one< '.' > >, seps, name > {};
-   struct variable_tail : pegtl::sor< variable_tail_one, variable_tail_two > {};
-
-   struct function_call_tail_one : pegtl::if_must< pegtl::seq< pegtl::not_at< pegtl::two< ':' > >, pegtl::one< ':' > >, seps, name, seps, function_args > {};
-   struct function_call_tail : pegtl::sor< function_args, function_call_tail_one > {};
-
-   struct variable_head_one : pegtl::seq< bracket_expr, seps, variable_tail > {};
-   struct variable_head : pegtl::sor< name, variable_head_one > {};
-
-   struct function_call_head : pegtl::sor< name, bracket_expr > {};
-
-   struct variable : pegtl::seq< variable_head, pegtl::star< pegtl::star< seps, function_call_tail >, seps, variable_tail > > {};
-   struct function_call : pegtl::seq< function_call_head, pegtl::plus< pegtl::until< pegtl::seq< seps, function_call_tail >, seps, variable_tail > > > {};
-
-   template< typename S, typename O >
-   struct left_assoc : pegtl::seq< S, seps, pegtl::star_must< O, seps, S, seps > > {};
-   template< typename S, typename O >
-   struct right_assoc : pegtl::seq< S, seps, pegtl::opt_must< O, seps, right_assoc< S, O > > > {};
-
-   struct unary_operators : pegtl::sor< pegtl::one< '-' >,
-                                        pegtl::one< '#' >,
-                                        op_one< '~', '=' >,
-                                        key_not > {};
-
-   struct expr_ten;
-   struct expr_thirteen : pegtl::seq< pegtl::sor< bracket_expr, name >, pegtl::star< seps, pegtl::sor< function_call_tail, variable_tail > > > {};
-   struct expr_twelve : pegtl::sor< key_nil,
-                                    key_true,
-                                    key_false,
-                                    pegtl::ellipsis,
-                                    numeral,
-                                    literal_string,
-                                    function_literal,
-                                    expr_thirteen,
-                                    table_constructor > {};
-   struct expr_eleven : pegtl::seq< expr_twelve, seps, pegtl::opt< pegtl::one< '^' >, seps, expr_ten, seps > > {};
-   struct unary_apply : pegtl::if_must< unary_operators, seps, expr_ten, seps > {};
-   struct expr_ten : pegtl::sor< unary_apply, expr_eleven > {};
-   struct operators_nine : pegtl::sor< pegtl::two< '/' >,
-                                       pegtl::one< '/' >,
-                                       pegtl::one< '*' >,
-                                       pegtl::one< '%' > > {};
-   struct expr_nine : left_assoc< expr_ten, operators_nine > {};
-   struct operators_eight : pegtl::sor< pegtl::one< '+' >,
-                                        pegtl::one< '-' > > {};
-   struct expr_eight : left_assoc< expr_nine, operators_eight > {};
-   struct expr_seven : right_assoc< expr_eight, op_two< '.', '.', '.' > > {};
-   struct operators_six : pegtl::sor< pegtl::two< '<' >,
-                                      pegtl::two< '>' > > {};
-   struct expr_six : left_assoc< expr_seven, operators_six > {};
-   struct expr_five : left_assoc< expr_six, pegtl::one< '&' > > {};
-   struct expr_four : left_assoc< expr_five, op_one< '~', '=' > > {};
-   struct expr_three : left_assoc< expr_four, pegtl::one< '|' > > {};
-   struct operators_two : pegtl::sor< pegtl::two< '=' >,
-                                      pegtl::string< '<', '=' >,
-                                      pegtl::string< '>', '=' >,
-                                      op_one< '<', '<' >,
-                                      op_one< '>', '>' >,
-                                      pegtl::string< '~', '=' > > {};
-   struct expr_two : left_assoc< expr_three, operators_two > {};
-   struct expr_one : left_assoc< expr_two, key_and > {};
-   struct expression : left_assoc< expr_one, key_or > {};
-
-   struct do_statement : pegtl::if_must< key_do, statement_list< key_end > > {};
-   struct while_statement : pegtl::if_must< key_while, seps, expression, seps, key_do, statement_list< key_end > > {};
-   struct repeat_statement : pegtl::if_must< key_repeat, statement_list< key_until >, seps, expression > {};
-
-   struct at_elseif_else_end : pegtl::sor< pegtl::at< key_elseif >, pegtl::at< key_else >, pegtl::at< key_end > > {};
-   struct elseif_statement : pegtl::if_must< key_elseif, seps, expression, seps, key_then, statement_list< at_elseif_else_end > > {};
-   struct else_statement : pegtl::if_must< key_else, statement_list< key_end > > {};
-   struct if_statement : pegtl::if_must< key_if, seps, expression, seps, key_then, statement_list< at_elseif_else_end >, seps, pegtl::until< pegtl::sor< else_statement, key_end >, elseif_statement, seps > > {};
-
-   struct for_statement_one : pegtl::seq< pegtl::one< '=' >, seps, expression, seps, pegtl::one< ',' >, seps, expression, pegtl::pad_opt< pegtl::if_must< pegtl::one< ',' >, seps, expression >, sep > > {};
-   struct for_statement_two : pegtl::seq< pegtl::opt_must< pegtl::one< ',' >, seps, name_list_must, seps >, key_in, seps, expr_list_must, seps > {};
-   struct for_statement : pegtl::if_must< key_for, seps, name, seps, pegtl::sor< for_statement_one, for_statement_two >, key_do, statement_list< key_end > > {};
-
-   struct assignment_variable_list : pegtl::list_must< variable, pegtl::one< ',' >, sep > {};
-   struct assignments_one : pegtl::if_must< pegtl::one< '=' >, seps, expr_list_must > {};
-   struct assignments : pegtl::seq< assignment_variable_list, seps, assignments_one > {};
-   struct function_name : pegtl::seq< pegtl::list< name, pegtl::one< '.' >, sep >, seps, pegtl::opt_must< pegtl::one< ':' >, seps, name, seps > > {};
-   struct function_definition : pegtl::if_must< key_function, seps, function_name, function_body > {};
-
-   struct local_function : pegtl::if_must< key_function, seps, name, seps, function_body > {};
-   struct local_variables : pegtl::if_must< name_list_must, seps, pegtl::opt< assignments_one > > {};
-   struct local_statement : pegtl::if_must< key_local, seps, pegtl::sor< local_function, local_variables > > {};
-
-   struct semicolon : pegtl::one< ';' > {};
-   struct statement : pegtl::sor< semicolon,
-                                  assignments,
-                                  function_call,
-                                  label_statement,
-                                  key_break,
-                                  goto_statement,
-                                  do_statement,
-                                  while_statement,
-                                  repeat_statement,
-                                  if_statement,
-                                  for_statement,
-                                  function_definition,
-                                  local_statement > {};
-
-   struct interpreter : pegtl::seq< pegtl::one< '#' >, pegtl::until< pegtl::eolf > > {};
-   struct grammar : pegtl::must< pegtl::opt< interpreter >, statement_list< pegtl::eof > > {};
-   // clang-format on
-
-}  // namespace lua53
+#include "lua53.hpp"
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
-   if( TAO_PEGTL_NAMESPACE::analyze< lua53::grammar >() != 0 ) {
-      return 1;
-   }
-
    for( int i = 1; i < argc; ++i ) {
       TAO_PEGTL_NAMESPACE::file_input in( argv[ i ] );
-      TAO_PEGTL_NAMESPACE::parse< lua53::grammar >( in );
+      const auto r = TAO_PEGTL_NAMESPACE::parse< lua53::grammar >( in );
+      std::cout << argv[ i ] << " " << r << std::endl;
    }
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp b/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a9303d54ab36b514b7856e36cc3b44d2426b55b3
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/lua53_print_debug.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl/contrib/print.hpp>
+
+#include "lua53.hpp"
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   tao::pegtl::print_debug< lua53::grammar >( std::cout );
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp b/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..39bb917e710c1c8f6500279cd5567de3b92c6494
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/lua53_print_names.cpp
@@ -0,0 +1,14 @@
+// Copyright (c) 2015-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl/contrib/print.hpp>
+
+#include "lua53.hpp"
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   tao::pegtl::print_names< lua53::grammar >( std::cout );
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/parse_tree.cpp b/packages/PEGTL/src/example/pegtl/parse_tree.cpp
index 9f0053f225a5d2bce91c51ce0e6489edf8d93547..1ccde1fef374eeb6ad47918a684bb9e2c8e24b61 100644
--- a/packages/PEGTL/src/example/pegtl/parse_tree.cpp
+++ b/packages/PEGTL/src/example/pegtl/parse_tree.cpp
@@ -2,6 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include <array>
+#include <iomanip>
 #include <iostream>
 #include <string>
 #include <type_traits>
@@ -174,10 +175,10 @@ int main( int argc, char** argv )
       return 0;
    }
    catch( const parse_error& e ) {
-      const auto p = e.positions.front();
+      const auto p = e.positions().front();
       std::cerr << e.what() << std::endl
                 << in.line_at( p ) << std::endl
-                << std::string( p.byte_in_line, ' ' ) << '^' << std::endl;
+                << std::setw( p.column ) << '^' << std::endl;
    }
    catch( const std::exception& e ) {
       std::cerr << e.what() << std::endl;
diff --git a/packages/PEGTL/src/example/pegtl/recover.cpp b/packages/PEGTL/src/example/pegtl/recover.cpp
index 6353a7442074a645a105e31999a2400bd3331adb..c76a5f9f3199700425203f8d1050d1dcec21ab6c 100644
--- a/packages/PEGTL/src/example/pegtl/recover.cpp
+++ b/packages/PEGTL/src/example/pegtl/recover.cpp
@@ -69,7 +69,7 @@ struct found
    static void apply( const ActionInput& in, bool& error )
    {
       if( !error ) {
-         std::cout << in.position() << ": Found " << internal::demangle< R >() << ": \"" << in.string() << "\"" << std::endl;
+         std::cout << in.position() << ": Found " << tao::demangle< R >() << ": \"" << in.string() << "\"" << std::endl;
       }
    }
 };
@@ -101,10 +101,10 @@ struct my_control
    : normal< Rule >
 {
    template< typename ParseInput, typename... States >
-   [[noreturn]] static void raise( const ParseInput& in, States&&... /*unused*/ )
+   [[noreturn]] static void raise( const ParseInput& in, States&&... st )
    {
-      std::cout << in.position() << ": Parse error matching " << internal::demangle< Rule >() << std::endl;
-      throw parse_error( "parse error matching " + std::string( internal::demangle< Rule >() ), in );
+      std::cout << in.position() << ": Parse error matching " << tao::demangle< Rule >() << std::endl;
+      normal< Rule >::raise( in, st... );
    }
 };
 
diff --git a/packages/PEGTL/src/example/pegtl/s_expression.cpp b/packages/PEGTL/src/example/pegtl/s_expression.cpp
index 7c47f1c8b9b11217a070b5ac6d8355beca569f02..8eb4204708e80a9653613de26bfb9f108115c649 100644
--- a/packages/PEGTL/src/example/pegtl/s_expression.cpp
+++ b/packages/PEGTL/src/example/pegtl/s_expression.cpp
@@ -6,35 +6,33 @@
 #include <tao/pegtl.hpp>
 #include <tao/pegtl/contrib/analyze.hpp>
 
-using namespace TAO_PEGTL_NAMESPACE;
-
 namespace sexpr
 {
    // clang-format off
-   struct hash_comment : until< eolf > {};
+   struct hash_comment : tao::pegtl::until< tao::pegtl::eolf > {};
 
    struct list;
-   struct list_comment : if_must< at< one< '(' > >, disable< list > > {};
+   struct list_comment : tao::pegtl::if_must< tao::pegtl::at< tao::pegtl::one< '(' > >, tao::pegtl::disable< list > > {};
 
-   struct read_include : seq< one< ' ' >, one< '"' >, plus< not_one< '"' > >, one< '"' > > {};
-   struct hash_include : if_must< string< 'i', 'n', 'c', 'l', 'u', 'd', 'e' >, read_include > {};
+   struct read_include : tao::pegtl::seq< tao::pegtl::one< ' ' >, tao::pegtl::one< '"' >, tao::pegtl::plus< tao::pegtl::not_one< '"' > >, tao::pegtl::one< '"' > > {};
+   struct hash_include : tao::pegtl::if_must< tao::pegtl::string< 'i', 'n', 'c', 'l', 'u', 'd', 'e' >, read_include > {};
 
-   struct hashed : if_must< one< '#' >, sor< hash_include, list_comment, hash_comment > > {};
+   struct hashed : tao::pegtl::if_must< tao::pegtl::one< '#' >, tao::pegtl::sor< hash_include, list_comment, hash_comment > > {};
 
-   struct number : plus< digit > {};
-   struct symbol : identifier {};
+   struct number : tao::pegtl::plus< tao::pegtl::digit > {};
+   struct symbol : tao::pegtl::identifier {};
 
-   struct atom : sor< number, symbol > {};
+   struct atom : tao::pegtl::sor< number, symbol > {};
 
    struct anything;
 
-   struct list : if_must< one< '(' >, until< one< ')' >, anything > > {};
+   struct list : tao::pegtl::if_must< tao::pegtl::one< '(' >, tao::pegtl::until< tao::pegtl::one< ')' >, anything > > {};
 
-   struct normal : sor< atom, list > {};
+   struct normal : tao::pegtl::sor< atom, list > {};
 
-   struct anything : sor< space, hashed, normal > {};
+   struct anything : tao::pegtl::sor< tao::pegtl::space, hashed, normal > {};
 
-   struct main : until< eof, must< anything > > {};
+   struct main : tao::pegtl::until< tao::pegtl::eof, tao::pegtl::must< anything > > {};
    // clang-format on
 
    template< typename Rule >
@@ -42,7 +40,7 @@ namespace sexpr
    {};
 
    template<>
-   struct action< plus< not_one< '"' > > >
+   struct action< tao::pegtl::plus< tao::pegtl::not_one< '"' > > >
    {
       template< typename ActionInput >
       static void apply( const ActionInput& in, std::string& fn )
@@ -64,8 +62,8 @@ namespace sexpr
          // last string literal that we use as filename here, and
          // the input is passed on for chained error messages (as
          // in "error in line x file foo included from file bar...)
-         file_input i2( fn );
-         parse_nested< main, sexpr::action >( in, i2, f2 );
+         tao::pegtl::file_input i2( fn );
+         tao::pegtl::parse_nested< main, sexpr::action >( in, i2, f2 );
       }
    };
 
@@ -73,14 +71,13 @@ namespace sexpr
 
 int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
 {
-   if( analyze< sexpr::main >() != 0 ) {
+   if( tao::pegtl::analyze< sexpr::main >() != 0 ) {
       return 1;
    }
-
    for( int i = 1; i < argc; ++i ) {
       std::string fn;
-      argv_input in( argv, i );
-      parse< sexpr::main, sexpr::action >( in, fn );
+      tao::pegtl::argv_input in( argv, i );
+      tao::pegtl::parse< sexpr::main, sexpr::action >( in, fn );
    }
    return 0;
 }
diff --git a/packages/PEGTL/src/example/pegtl/symbol_table.cpp b/packages/PEGTL/src/example/pegtl/symbol_table.cpp
index 5d0a7b51ab7a503008cc787c3ade45738dcf4af2..75a520135bc31b9202ef01e6c6673cb1ca8f5f83 100644
--- a/packages/PEGTL/src/example/pegtl/symbol_table.cpp
+++ b/packages/PEGTL/src/example/pegtl/symbol_table.cpp
@@ -47,9 +47,12 @@ namespace example
 
    template<>
    struct action< value >
-      : public pegtl::unsigned_action
    {
-      // Sets st.converted to the integer value of the matched string.
+      template< typename ActionInput >
+      static void apply( const ActionInput& in, state& st )
+      {
+         pegtl::unsigned_action::apply( in, st.converted );
+      }
    };
 
    template<>
diff --git a/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp b/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..25858179b541efb9f646892944be31b630b55315
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/uri_print_debug.cpp
@@ -0,0 +1,13 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl/contrib/print.hpp>
+#include <tao/pegtl/contrib/uri.hpp>
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   tao::pegtl::print_debug< tao::pegtl::uri::URI >( std::cout );
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/uri_print_names.cpp b/packages/PEGTL/src/example/pegtl/uri_print_names.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6867333364e527b20fe24fd76e72f3e33f8368d9
--- /dev/null
+++ b/packages/PEGTL/src/example/pegtl/uri_print_names.cpp
@@ -0,0 +1,13 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+
+#include <tao/pegtl/contrib/print.hpp>
+#include <tao/pegtl/contrib/uri.hpp>
+
+int main()  // NOLINT(bugprone-exception-escape)
+{
+   tao::pegtl::print_names< tao::pegtl::uri::URI >( std::cout );
+   return 0;
+}
diff --git a/packages/PEGTL/src/example/pegtl/uri_trace.cpp b/packages/PEGTL/src/example/pegtl/uri_trace.cpp
index cbd354ef15277ed3d126633757fd0adcc9316bc3..14a3a29588640562d1f2f73d4ad7cfb71c7abaf0 100644
--- a/packages/PEGTL/src/example/pegtl/uri_trace.cpp
+++ b/packages/PEGTL/src/example/pegtl/uri_trace.cpp
@@ -16,7 +16,7 @@ int main( int argc, char** argv )  // NOLINT(bugprone-exception-escape)
    for( int i = 1; i < argc; ++i ) {
       std::cout << "Parsing " << argv[ i ] << std::endl;
       pegtl::argv_input in( argv, i );
-      pegtl::parse< grammar, pegtl::nothing, pegtl::trace_control >( in );
+      pegtl::complete_trace< grammar >( in );
    }
    return 0;
 }
diff --git a/packages/PEGTL/src/test/pegtl/CMakeLists.txt b/packages/PEGTL/src/test/pegtl/CMakeLists.txt
index 964d99432ce8e434ccc7e807aaec5aaeb376cf92..446ef2921f64949a984155553cf0c099a6bcecf3 100644
--- a/packages/PEGTL/src/test/pegtl/CMakeLists.txt
+++ b/packages/PEGTL/src/test/pegtl/CMakeLists.txt
@@ -25,6 +25,7 @@ set(test_sources
   change_states.cpp
   contrib_alphabet.cpp
   contrib_analyze.cpp
+  contrib_control_action.cpp
   contrib_http.cpp
   contrib_if_then.cpp
   contrib_integer.cpp
@@ -33,13 +34,16 @@ set(test_sources
   contrib_partial_trace.cpp
   contrib_raw_string.cpp
   contrib_rep_one_min_max.cpp
+  contrib_state_control.cpp
   contrib_to_string.cpp
   contrib_tracer.cpp
   contrib_unescape.cpp
   contrib_uri.cpp
+  control_unwind.cpp
   data_cstring.cpp
   demangle.cpp
   discard_input.cpp
+  enable_control.cpp
   error_message.cpp
   file_cstream.cpp
   file_file.cpp
@@ -49,6 +53,7 @@ set(test_sources
   internal_endian.cpp
   internal_file_mapper.cpp
   internal_file_opener.cpp
+  parse_error.cpp
   pegtl_string_t.cpp
   position.cpp
   rule_action.cpp
@@ -60,6 +65,7 @@ set(test_sources
   rule_bytes.cpp
   rule_control.cpp
   rule_disable.cpp
+  rule_discard.cpp
   rule_enable.cpp
   rule_eof.cpp
   rule_failure.cpp
@@ -78,6 +84,7 @@ set(test_sources
   rule_pad.cpp
   rule_pad_opt.cpp
   rule_plus.cpp
+  rule_raise.cpp
   rule_rematch.cpp
   rule_rep.cpp
   rule_rep_max.cpp
diff --git a/packages/PEGTL/src/test/pegtl/actions_one.cpp b/packages/PEGTL/src/test/pegtl/actions_one.cpp
index 1e5c2f81312439714ad6cd5475cde7480431e370..40128c1680bcc28b83eb49d148790449454cffc2 100644
--- a/packages/PEGTL/src/test/pegtl/actions_one.cpp
+++ b/packages/PEGTL/src/test/pegtl/actions_one.cpp
@@ -29,16 +29,16 @@ namespace TAO_PEGTL_NAMESPACE
       {
          TAO_PEGTL_TEST_ASSERT( applied.size() == 10 );
 
-         TAO_PEGTL_TEST_ASSERT( applied[ 0 ].first == internal::demangle< one< 'b' > >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 1 ].first == internal::demangle< foo >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 2 ].first == internal::demangle< at< one< 'a' > > >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 3 ].first == internal::demangle< two< 'a' > >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 4 ].first == internal::demangle< fiz >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 5 ].first == internal::demangle< foo >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 6 ].first == internal::demangle< one< 'b' > >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 7 ].first == internal::demangle< foo >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 8 ].first == internal::demangle< eof >() );
-         TAO_PEGTL_TEST_ASSERT( applied[ 9 ].first == internal::demangle< bar >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 0 ].first == tao::demangle< one< 'b' > >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 1 ].first == tao::demangle< foo >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 2 ].first == tao::demangle< at< one< 'a' > > >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 3 ].first == tao::demangle< two< 'a' > >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 4 ].first == tao::demangle< fiz >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 5 ].first == tao::demangle< foo >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 6 ].first == tao::demangle< one< 'b' > >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 7 ].first == tao::demangle< foo >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 8 ].first == tao::demangle< eof >() );
+         TAO_PEGTL_TEST_ASSERT( applied[ 9 ].first == tao::demangle< bar >() );
 
          TAO_PEGTL_TEST_ASSERT( applied[ 0 ].second == "b" );
          TAO_PEGTL_TEST_ASSERT( applied[ 1 ].second == "b" );
@@ -60,7 +60,7 @@ namespace TAO_PEGTL_NAMESPACE
       template< typename ActionInput >
       static void apply( const ActionInput& in )
       {
-         applied.emplace_back( internal::demangle< Rule >(), in.string() );
+         applied.emplace_back( tao::demangle< Rule >(), in.string() );
       }
    };
 
@@ -69,7 +69,7 @@ namespace TAO_PEGTL_NAMESPACE
       parse< disable< test1::bar >, test_action >( memory_input( "baab", __FUNCTION__ ) );
       TAO_PEGTL_TEST_ASSERT( applied.size() == 1 );
 
-      TAO_PEGTL_TEST_ASSERT( applied[ 0 ].first == internal::demangle< disable< test1::bar > >() );
+      TAO_PEGTL_TEST_ASSERT( applied[ 0 ].first == tao::demangle< disable< test1::bar > >() );
       TAO_PEGTL_TEST_ASSERT( applied[ 0 ].second == "baab" );
 
       applied.clear();
diff --git a/packages/PEGTL/src/test/pegtl/actions_two.cpp b/packages/PEGTL/src/test/pegtl/actions_two.cpp
index a21d222ff32abb8014c7e0f9a6906298ccf8c2d3..d3dcb224ee3d83324ca39e5eebd72ea6d95338da 100644
--- a/packages/PEGTL/src/test/pegtl/actions_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/actions_two.cpp
@@ -91,7 +91,7 @@ namespace TAO_PEGTL_NAMESPACE
 
       const std::size_t count_byte = 12345;
       const std::size_t count_line = 42;
-      const std::size_t count_byte_in_line = 12;
+      const std::size_t count_column = 12;
 
       const char* count_source = "count_source";
 
@@ -103,7 +103,7 @@ namespace TAO_PEGTL_NAMESPACE
          {
             TAO_PEGTL_TEST_ASSERT( in.iterator().byte == count_byte );
             TAO_PEGTL_TEST_ASSERT( in.iterator().line == count_line );
-            TAO_PEGTL_TEST_ASSERT( in.iterator().byte_in_line == count_byte_in_line );
+            TAO_PEGTL_TEST_ASSERT( in.iterator().column == count_column );
             TAO_PEGTL_TEST_ASSERT( in.input().source() == count_source );
             TAO_PEGTL_TEST_ASSERT( in.size() == 1 );
             TAO_PEGTL_TEST_ASSERT( in.begin() + 1 == in.end() );
@@ -115,7 +115,7 @@ namespace TAO_PEGTL_NAMESPACE
       void count_test()
       {
          const char* foo = "f";
-         memory_input in( foo, foo + 1, count_source, count_byte, count_line, count_byte_in_line );
+         memory_input in( foo, foo + 1, count_source, count_byte, count_line, count_column );
          const auto result = parse< must< alpha >, count_action >( in );
          TAO_PEGTL_TEST_ASSERT( result );
       }
diff --git a/packages/PEGTL/src/test/pegtl/ascii_classes.cpp b/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
index c3301af9784c8cdd7dc3312b0945fc4f82452222..8271091cbe15d587337bf8d17b5016d758a096eb 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_classes.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_eol.cpp b/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
index 9724234d375da805ad1ae7de7de235b3ffe0921f..2d46c03a5b49600fd060201f49afc863c43862f4 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_eol.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp b/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
index 2cb54e528a1390a4e1f1f72490c8fa39344b9f16..c91c155100b4e230c32cf559d5f6b6b3c26201c6 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_eolf.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp b/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
index fcce5471fefbd91eaa0a07bf5b8b2b297e6a3687..1bbc0c01db01dda98b504969936f5971fa05783f 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_forty_two.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp b/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
index 3a4fcbceb5cf43451afabe5469e5f91c60052fa1..5d521d4d2745c74b286ef486b0f08cfd7524f3d0 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_identifier.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_istring.cpp b/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
index 73a9e733fbc181586a27e095cb524354c5239f92..b29b8a6eceafe1771a7f2b0d8b7e3334954b3e4d 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_istring.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp b/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
index 17746344dbd488e701312e6684a6adb1d1b83e80..6959625add9b26b1a96ea6828573039e4ad33591 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_keyword.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp b/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
index 57042c9a2f0c7cae8c188d8f4238e530339f48c5..47d9bc9b3a82dcfb6a73e7c81c84f18dd5e3ea9a 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_shebang.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_string.cpp b/packages/PEGTL/src/test/pegtl/ascii_string.cpp
index 6d24356c9ac5e4d416751f8ffd724710b1ba5d80..bc97c6f947902773937886792ee2e6937dc0bc63 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_string.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_three.cpp b/packages/PEGTL/src/test/pegtl/ascii_three.cpp
index 23a9226306333bf57e75311db35c7b0c9634c5c5..e6341dcc4bb1df8c019324eabd0c5434ff5494e6 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_three.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_three.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/ascii_two.cpp b/packages/PEGTL/src/test/pegtl/ascii_two.cpp
index 020954b185a013a436acb149209f4df77c207788..5ca9d36b00716d44f8ed924e1ef421c648bbba19 100644
--- a/packages/PEGTL/src/test/pegtl/ascii_two.cpp
+++ b/packages/PEGTL/src/test/pegtl/ascii_two.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp b/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
index dee2d0408fa9d1188cb7cd556b53625e6092710a..65f1d4831fc4be550caf3fb191114105fe7e4af0 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_analyze.cpp
@@ -2,12 +2,35 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
+   template< typename... Rules >
+   struct strange
+   {
+      using rule_t = strange;
+      using subs_t = type_list< Rules... >;
+
+      static_assert( sizeof...( Rules ) > 1 );
+
+      // Pretend to be a rule that consumes on success by itself _and_ has sub-rules,
+      // to test a supported combination even though it is not frequently encountered.
+   };
+
+   template< typename Name, typename... Rules >
+   struct analyze_traits< Name, strange< Rules... > >
+      : analyze_any_traits< Rules... >
+   {};
+
    void unit_test()
    {
+      verify_analyze< strange< alpha, digit > >( __LINE__, __FILE__, true, false );
+      verify_analyze< strange< opt< alpha >, opt< digit > > >( __LINE__, __FILE__, true, false );
+
+      verify_analyze< strange< star< star< alpha > >, digit > >( __LINE__, __FILE__, true, true );
+      verify_analyze< strange< digit, star< star< alpha > > > >( __LINE__, __FILE__, true, true );
+
       verify_analyze< eof >( __LINE__, __FILE__, false, false );
       verify_analyze< eolf >( __LINE__, __FILE__, false, false );
       verify_analyze< success >( __LINE__, __FILE__, false, false );
diff --git a/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp b/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1d03133953e1cfaf098ea167dcb30be1b4372bd9
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/contrib_control_action.cpp
@@ -0,0 +1,140 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <string>
+
+#include "test.hpp"
+
+#include <tao/pegtl/contrib/control_action.hpp>
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   template< typename Rule >
+   struct test_action
+      : nothing< Rule >
+   {};
+
+   struct first_rule
+      : sor< alpha, digit >
+   {};
+
+   struct second_rule
+      : must< alnum >
+   {};
+
+   std::string story;
+
+   template<>
+   struct test_action< first_rule >
+      : control_action
+   {
+      template< typename ParseInput >
+      static void start( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'a';
+      }
+
+      template< typename ParseInput >
+      static void success( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'b';
+      }
+
+      template< typename ParseInput >
+      static void failure( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'c';
+      }
+   };
+
+   template<>
+   struct test_action< alpha >
+      : control_action
+   {
+      template< typename ParseInput >
+      static void start( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'd';
+      }
+
+      template< typename ParseInput >
+      static void success( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'e';
+      }
+      template< typename ParseInput >
+      static void failure( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'f';
+      }
+   };
+
+   template<>
+   struct test_action< digit >
+      : control_action
+   {
+      template< typename ParseInput >
+      static void start( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'g';
+      }
+
+      template< typename ParseInput >
+      static void success( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'h';
+      }
+      template< typename ParseInput >
+      static void failure( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'i';
+      }
+   };
+
+   template<>
+   struct test_action< second_rule >
+      : control_action
+   {
+      template< typename ParseInput >
+      static void start( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'j';
+      }
+
+      template< typename ParseInput >
+      static void success( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'k';
+      }
+      template< typename ParseInput >
+      static void failure( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'l';
+      }
+
+      template< typename ParseInput >
+      static void unwind( const ParseInput& /*unused*/, int /*unused*/ )
+      {
+         story += 'm';
+      }
+   };
+
+   void unit_test()
+   {
+      {
+         memory_input in( "0", __FUNCTION__ );
+         const auto b = parse< first_rule, test_action >( in, 42 );
+         TAO_PEGTL_TEST_ASSERT( b );
+         TAO_PEGTL_TEST_ASSERT( story == "adfghb" );
+      }
+      story.clear();
+      {
+         memory_input in( "*", __FUNCTION__ );
+         TAO_PEGTL_TEST_THROWS( parse< second_rule, test_action >( in, 42 ) );
+         TAO_PEGTL_TEST_ASSERT( story == "jm" );
+      }
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_http.cpp b/packages/PEGTL/src/test/pegtl/contrib_http.cpp
index b4f74cd5adc0e3c5a2cdceccd8351e38a0ca0272..0273d43b10a34b25b94bf35bda4bc5ffaa0f4639 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_http.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_http.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 
 #include <tao/pegtl/contrib/http.hpp>
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_integer.cpp b/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
index 9290f670de8575ba55f0feacaeaaee80912ac2c2..0a9cc8eaf857e700bb96b0ea237e5dee69b506e7 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_integer.cpp
@@ -6,19 +6,13 @@
 
 #include "test.hpp"
 
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 #include <tao/pegtl/contrib/integer.hpp>
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   template< typename I >
-   struct int_state
-   {
-      I converted = 55;
-   };
-
    template< typename Rule >
    struct int_action
       : nothing< Rule >
@@ -43,12 +37,6 @@ namespace TAO_PEGTL_NAMESPACE
          parse< must< signed_rule, eof >, int_action >( in, st );
          TAO_PEGTL_TEST_ASSERT( st == s );
       }
-      {
-         int_state< S > st;
-         memory_input in( i, __FUNCTION__ );
-         parse< must< signed_rule, eof >, int_action >( in, st );
-         TAO_PEGTL_TEST_ASSERT( st.converted == s );
-      }
       {
          S st = -123;
          memory_input in( i, __FUNCTION__ );
@@ -60,7 +48,7 @@ namespace TAO_PEGTL_NAMESPACE
    template< typename S >
    void test_signed( const std::string& i )
    {
-      int_state< S > st;
+      S st;
       memory_input in( i, __FUNCTION__ );
       TAO_PEGTL_TEST_THROWS( parse< must< signed_rule, eof >, int_action >( in, st ) );
    }
@@ -76,11 +64,11 @@ namespace TAO_PEGTL_NAMESPACE
    template< typename S >
    void test_signed( const S s )
    {
-      int_state< S > st;
+      S st;
       const auto i = lexical_cast( s );
       memory_input in( i, __FUNCTION__ );
       parse< must< signed_rule, eof >, int_action >( in, st );
-      TAO_PEGTL_TEST_ASSERT( st.converted == s );
+      TAO_PEGTL_TEST_ASSERT( st == s );
    }
 
    template< typename S >
@@ -92,12 +80,6 @@ namespace TAO_PEGTL_NAMESPACE
          parse< must< unsigned_rule, eof >, int_action >( in, st );
          TAO_PEGTL_TEST_ASSERT( st == s );
       }
-      {
-         int_state< S > st;
-         memory_input in( i, __FUNCTION__ );
-         parse< must< unsigned_rule, eof >, int_action >( in, st );
-         TAO_PEGTL_TEST_ASSERT( st.converted == s );
-      }
       {
          S st = 123;
          memory_input in( i, __FUNCTION__ );
diff --git a/packages/PEGTL/src/test/pegtl/contrib_json.cpp b/packages/PEGTL/src/test/pegtl/contrib_json.cpp
index 67abd1d1d9083abeb67e8d68ed0a5080fa6312b3..2cd30e1320783cbf066e844b8f3d40168b5b41b2 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_json.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_json.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_fail.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 #include <tao/pegtl/contrib/json.hpp>
diff --git a/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp b/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
index 521a8140cdb8d7eaac1c78d370b38ee9cbbbf8f9..bce6834501b9d887fe3cae3bc773a6f6d8998cff 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_parse_tree.cpp
@@ -13,6 +13,8 @@ namespace TAO_PEGTL_NAMESPACE
    struct C : one< 'c' > {};
 
    struct D : sor< seq< A, B >, seq< A, C > > {};
+
+   struct D2 : sor< try_catch< if_must< A, B > >, seq< A, C > > {};
    // clang-format on
 
    template< typename Rule >
@@ -22,28 +24,74 @@ namespace TAO_PEGTL_NAMESPACE
 
    void unit_test()
    {
-      memory_input in( "ac", "input" );
-      const auto r = parse_tree::parse< D, selector >( in );
-      TAO_PEGTL_TEST_ASSERT( r->is_root() );
-      TAO_PEGTL_TEST_ASSERT( !r->has_content() );
-      TAO_PEGTL_TEST_ASSERT( r->children.size() == 1 );
-
-      const auto& d = r->children.front();
-      TAO_PEGTL_TEST_ASSERT( !d->is_root() );
-      TAO_PEGTL_TEST_ASSERT( d->is_type< D >() );
-
-      TAO_PEGTL_TEST_ASSERT( d->has_content() );
-      TAO_PEGTL_TEST_ASSERT( d->begin().byte == 0 );
-      TAO_PEGTL_TEST_ASSERT( d->end().byte == 2 );
-      TAO_PEGTL_TEST_ASSERT( d->string_view() == "ac" );
-
-      TAO_PEGTL_TEST_ASSERT( d->children.size() == 2 );
-      TAO_PEGTL_TEST_ASSERT( d->children.front()->is_type< A >() );
-      TAO_PEGTL_TEST_ASSERT( d->children.back()->is_type< C >() );
-
-      memory_input in2( "x", "input" );
-      const auto r2 = parse_tree::parse< D, selector >( in2 );
-      TAO_PEGTL_TEST_ASSERT( !r2 );
+      {
+         memory_input in( "ac", "input" );
+         const auto r = parse_tree::parse< D, selector >( in );
+         TAO_PEGTL_TEST_ASSERT( r );
+         TAO_PEGTL_TEST_ASSERT( r->is_root() );
+         TAO_PEGTL_TEST_ASSERT( !r->has_content() );
+         TAO_PEGTL_TEST_ASSERT( r->children.size() == 1 );
+
+         const auto& d = r->children.front();
+         TAO_PEGTL_TEST_ASSERT( !d->is_root() );
+         TAO_PEGTL_TEST_ASSERT( d->is_type< D >() );
+
+         TAO_PEGTL_TEST_ASSERT( d->has_content() );
+         TAO_PEGTL_TEST_ASSERT( d->begin().byte == 0 );
+         TAO_PEGTL_TEST_ASSERT( d->end().byte == 2 );
+         TAO_PEGTL_TEST_ASSERT( d->string_view() == "ac" );
+
+         TAO_PEGTL_TEST_ASSERT( d->children.size() == 2 );
+         TAO_PEGTL_TEST_ASSERT( d->children.front()->is_type< A >() );
+         TAO_PEGTL_TEST_ASSERT( d->children.back()->is_type< C >() );
+
+         memory_input in2( "x", "input" );
+         const auto r2 = parse_tree::parse< D, selector >( in2 );
+         TAO_PEGTL_TEST_ASSERT( !r2 );
+      }
+
+      {
+         memory_input in( "ac", "input" );
+         const auto r = parse_tree::parse< D2, selector >( in );
+         TAO_PEGTL_TEST_ASSERT( r );
+         TAO_PEGTL_TEST_ASSERT( r->is_root() );
+         TAO_PEGTL_TEST_ASSERT( !r->has_content() );
+
+         TAO_PEGTL_TEST_ASSERT( r->children.size() == 2 );
+         TAO_PEGTL_TEST_ASSERT( r->children.front()->is_type< A >() );
+         TAO_PEGTL_TEST_ASSERT( r->children.back()->is_type< C >() );
+      }
+
+      {
+         memory_input in( "ac", "input" );
+         const auto r = parse_tree::parse< D2 >( in );
+         TAO_PEGTL_TEST_ASSERT( r );
+         TAO_PEGTL_TEST_ASSERT( r->is_root() );
+         TAO_PEGTL_TEST_ASSERT( !r->has_content() );
+         TAO_PEGTL_TEST_ASSERT( r->children.size() == 1 );
+
+         const auto& d2 = r->children.front();
+         TAO_PEGTL_TEST_ASSERT( !d2->is_root() );
+         TAO_PEGTL_TEST_ASSERT( d2->is_type< D2 >() );
+
+         TAO_PEGTL_TEST_ASSERT( d2->has_content() );
+         TAO_PEGTL_TEST_ASSERT( d2->begin().byte == 0 );
+         TAO_PEGTL_TEST_ASSERT( d2->end().byte == 2 );
+         TAO_PEGTL_TEST_ASSERT( d2->string() == "ac" );
+
+         const auto& internal = d2->children.front();
+         TAO_PEGTL_TEST_ASSERT( !internal->is_root() );
+         TAO_PEGTL_TEST_ASSERT( internal->is_type< seq< A, C > >() );
+
+         TAO_PEGTL_TEST_ASSERT( internal->has_content() );
+         TAO_PEGTL_TEST_ASSERT( internal->begin().byte == 0 );
+         TAO_PEGTL_TEST_ASSERT( internal->end().byte == 2 );
+         TAO_PEGTL_TEST_ASSERT( internal->string_view() == "ac" );
+
+         TAO_PEGTL_TEST_ASSERT( internal->children.size() == 2 );
+         TAO_PEGTL_TEST_ASSERT( internal->children.front()->is_type< A >() );
+         TAO_PEGTL_TEST_ASSERT( internal->children.back()->is_type< C >() );
+      }
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp b/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
index b8ac7a168bbedd7c66f3020e04e4b35793febec5..5743114608c7c0adabe767034808b880443d4799 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_partial_trace.cpp
@@ -8,17 +8,17 @@
 namespace TAO_PEGTL_NAMESPACE
 {
    // clang-format off
-   struct inner : seq< one< 'a' >, sor< one< 'b' >, one< 'c' > > > {};
+   struct inner : seq< one< 'a' >, sor< one< 'b' >, one< 'c' >, inner > > {};
    struct outer : seq< one< 'x' >, inner, one< 'y' > > {};
 
    // how to run a tracer on a *part* of the grammar:
    template< typename > struct partial_action {};
-   template<> struct partial_action< inner > : change_control< trace_control > {};
+   template<> struct partial_action< inner > : trace_standard {};
    // clang-format on
 
    void unit_test()
    {
-      memory_input in( "xacy", "trace test please ignore" );
+      memory_input in( "xaacy", "trace test please ignore" );
       const auto result = parse< outer, partial_action >( in );
       TAO_PEGTL_TEST_ASSERT( result );
    }
diff --git a/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp b/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
index 95d2737e72d37b5bea2ffce010c4e3e822503b72..6ae1980754fdc5f6402a5a8d96f437312063c00c 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_raw_string.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_fail.hpp"
+#include "verify_meta.hpp"
 
 #include <tao/pegtl/contrib/raw_string.hpp>
 
@@ -54,13 +54,13 @@ namespace TAO_PEGTL_NAMESPACE
    void verify_data( const std::size_t line, const char* file, const char ( &m )[ M ], const char ( &n )[ N ] )
    {
       content.clear();
-      memory_input in( m, m + M - 1, file, 0, line, 0 );
+      memory_input in( m, m + M - 1, file, 0, line, 1 );
       const auto r = parse< Rule, Action >( in );
       if( ( !r ) || ( content != std::string_view( n, N - 1 ) ) ) {
          TAO_PEGTL_TEST_FAILED( "input data [ '" << m << "' ] expected success with [ '" << n << "' ] but got [ '" << content << "' ] result [ " << r << " ]" );
       }
       content.clear();
-      memory_input< tracking_mode::lazy > in2( m, m + M - 1, file, 0, line, 0 );
+      memory_input< tracking_mode::lazy > in2( m, m + M - 1, file, 0, line, 1 );
       const auto r2 = parse< Rule, Action >( in2 );
       if( ( !r2 ) || ( content != std::string_view( n, N - 1 ) ) ) {
          TAO_PEGTL_TEST_FAILED( "input data [ '" << m << "' ] with tracking_mode::lazy expected success with [ '" << n << "' ] but got [ '" << content << "' ] result [ " << r2 << " ]" );
diff --git a/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp b/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
index b190a0cca7737bf17dd160fdcff57b9f50a491df..ee9ecf7b6c2efa06b9a2249169698f1f03061f12 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_rep_one_min_max.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 #include <tao/pegtl/contrib/rep_one_min_max.hpp>
diff --git a/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp b/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8dc07cf3460ae9082b9b47a1977a45233301b6ac
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/contrib_state_control.cpp
@@ -0,0 +1,190 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <iostream>
+#include <string_view>
+#include <vector>
+
+#include "test.hpp"
+
+#include <tao/pegtl/contrib/state_control.hpp>
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   struct test_entry
+   {
+      std::string_view rule;
+      std::string_view func;
+      std::size_t b;
+   };
+
+   inline bool operator==( const test_entry& l, const test_entry& r ) noexcept
+   {
+      return ( l.rule == r.rule ) && ( l.func == r.func ) && ( l.b == r.b );
+   }
+
+   template< bool E >
+   struct test_state
+   {
+      std::vector< test_entry > trace;
+
+      template< typename Rule >
+      static constexpr bool enable = E;
+
+      template< typename Rule, typename Input, typename... States >
+      void start( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "start", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void success( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "success", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void failure( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "failure", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void raise( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "raise", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void unwind( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "unwind", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void apply( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "apply", ++b } );
+      }
+
+      template< typename Rule, typename Input, typename... States >
+      void apply0( const Input& /*unused*/, const int a, std::size_t& b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         trace.push_back( { demangle< Rule >(), "apply0", ++b } );
+      }
+   };
+
+   struct test_grammar : must< sor< one< 'a' >, try_catch< seq< one< 'b' >, must< one< 'c' > > > >, two< 'b' > >, eof >
+   {};
+
+   template< typename Rule >
+   struct test_action
+      : nothing< Rule >
+   {};
+
+   bool test_action_one = false;
+
+   template<>
+   struct test_action< one< 'b' > >
+   {
+      static void apply0( const int a, const std::size_t b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         if( test_action_one ) {
+            TAO_PEGTL_TEST_ASSERT( b == 0 );
+            test_action_one = false;
+         }
+         else {
+            TAO_PEGTL_TEST_ASSERT( b == 9 );
+            test_action_one = true;
+         }
+      }
+   };
+
+   bool test_action_two = false;
+
+   template<>
+   struct test_action< two< 'b' > >
+   {
+      template< typename ActionInput >
+      static void apply( const ActionInput& /*unused*/, const int a, const std::size_t b )
+      {
+         TAO_PEGTL_TEST_ASSERT( a == -1 );
+         if( test_action_two ) {
+            TAO_PEGTL_TEST_ASSERT( b == 0 );
+            test_action_two = false;
+         }
+         else {
+            TAO_PEGTL_TEST_ASSERT( b == 19 );
+            test_action_two = true;
+         }
+      }
+   };
+
+   void unit_test()
+   {
+      {
+         test_state< true > st;
+         memory_input in( "bb", __FUNCTION__ );
+         std::size_t b = 0;
+         const bool result = parse< test_grammar, test_action, state_control< normal >::type >( in, -1, b, st );
+         TAO_PEGTL_TEST_ASSERT( result );
+         TAO_PEGTL_TEST_ASSERT( test_action_one );
+         TAO_PEGTL_TEST_ASSERT( test_action_two );
+         TAO_PEGTL_TEST_ASSERT( b == st.trace.size() );
+         b = 0;
+         std::size_t i = 0;
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< test_grammar >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< internal::must< sor< one< 'a' >, try_catch< seq< one< 'b' >, must< one< 'c' > > > >, two< 'b' > > > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< sor< one< 'a' >, try_catch< seq< one< 'b' >, must< one< 'c' > > > >, two< 'b' > > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'a' > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'a' > >(), "failure", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< try_catch< seq< one< 'b' >, must< one< 'c' > > > > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< seq< one< 'b' >, must< one< 'c' > > > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'b' > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'b' > >(), "apply0", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'b' > >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< must< one< 'c' > > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'c' > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'c' > >(), "failure", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< one< 'c' > >(), "raise", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< must< one< 'c' > > >(), "unwind", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< seq< one< 'b' >, must< one< 'c' > > > >(), "unwind", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< try_catch< seq< one< 'b' >, must< one< 'c' > > > > >(), "failure", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< two< 'b' > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< two< 'b' > >(), "apply", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< two< 'b' > >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< sor< one< 'a' >, try_catch< seq< one< 'b' >, must< one< 'c' > > > >, two< 'b' > > >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< internal::must< sor< one< 'a' >, try_catch< seq< one< 'b' >, must< one< 'c' > > > >, two< 'b' > > > >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< internal::must< eof > >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< eof >(), "start", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< eof >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< internal::must< eof > >(), "success", ++b } );
+         TAO_PEGTL_TEST_ASSERT( st.trace[ i++ ] == test_entry{ demangle< test_grammar >(), "success", ++b } );
+
+         TAO_PEGTL_TEST_ASSERT( i == st.trace.size() );
+         TAO_PEGTL_TEST_ASSERT( b == st.trace.size() );
+      }
+      {
+         test_state< false > st;
+         memory_input in( "bb", __FUNCTION__ );
+         std::size_t b = 0;
+         const bool result = parse< test_grammar, test_action, state_control< normal >::type >( in, -1, b, st );
+         TAO_PEGTL_TEST_ASSERT( result );
+         TAO_PEGTL_TEST_ASSERT( !test_action_one );
+         TAO_PEGTL_TEST_ASSERT( !test_action_two );
+         TAO_PEGTL_TEST_ASSERT( st.trace.empty() );
+         TAO_PEGTL_TEST_ASSERT( b == 0 );
+      }
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/contrib_tracer.cpp b/packages/PEGTL/src/test/pegtl/contrib_tracer.cpp
index 074c701f71d2eed0486754949762ede23348fd3b..a6f20b1a04b4d87febd4877f128a57cdeac3c339 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_tracer.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_tracer.cpp
@@ -7,8 +7,10 @@
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   using GRAMMAR = sor< failure, one< 'a' > >;
+   using GRAMMAR1 = sor< failure, one< 'a' > >;
    using GRAMMAR2 = seq< one< 'a' >, any, any, any, any, one< 'b' >, eof >;
+   using GRAMMAR3 = sor< one< 'a' >, one< 'b' > >;
+   using GRAMMAR4 = try_catch< sor< one< 'a' >, must< one< 'b' > > > >;
 
    template< typename Rule >
    struct trace_action
@@ -28,7 +30,7 @@ namespace TAO_PEGTL_NAMESPACE
    };
 
    template<>
-   struct trace_action< GRAMMAR >
+   struct trace_action< GRAMMAR1 >
    {
       template< typename... Ts >
       static void apply( Ts&&... /*unused*/ )
@@ -41,47 +43,41 @@ namespace TAO_PEGTL_NAMESPACE
    {
       {
          memory_input in( "ab", "trace test please ignore" );
-         const auto result = parse< GRAMMAR, nothing, trace_control >( in );
+         const auto result = standard_trace< GRAMMAR1 >( in );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( a0 == 0 );
          TAO_PEGTL_TEST_ASSERT( a == 0 );
       }
       {
          memory_input in( "ab", "trace test please ignore" );
-         const auto result = parse< GRAMMAR, trace_action, trace_control >( in );
+         const auto result = standard_trace< GRAMMAR1, trace_action >( in );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( a0 == 1 );
          TAO_PEGTL_TEST_ASSERT( a == 1 );
       }
       {
-         trace_state ts;
-         memory_input in( "ab", "trace test please ignore" );
-         const auto result = parse< GRAMMAR, nothing, trace_control >( in, ts );
+         memory_input in( "a\r\n\t\0b", 6, "trace test please ignore" );
+         const auto result = standard_trace< GRAMMAR2 >( in );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( a0 == 1 );
          TAO_PEGTL_TEST_ASSERT( a == 1 );
       }
       {
-         trace_state ts;
-         memory_input in( "ab", "trace test please ignore" );
-         const auto result = parse< GRAMMAR, trace_action, trace_control >( in, ts );
+         memory_input in( "a\r\n\t\0b", 6, "trace test please ignore" );
+         const auto result = standard_trace< GRAMMAR2, trace_action >( in );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( a0 == 2 );
-         TAO_PEGTL_TEST_ASSERT( a == 2 );
+         TAO_PEGTL_TEST_ASSERT( a == 1 );
       }
       {
-         trace_state ts;
-         memory_input in( "a\r\n\t\0b", 6, "trace test please ignore" );
-         const auto result = parse< GRAMMAR2, nothing, trace_control >( in, ts );
-         TAO_PEGTL_TEST_ASSERT( result );
-         TAO_PEGTL_TEST_ASSERT( a0 == 2 );
-         TAO_PEGTL_TEST_ASSERT( a == 2 );
+         memory_input in( "c", "trace test please ignore" );
+         const auto result = standard_trace< GRAMMAR3 >( in );
+         TAO_PEGTL_TEST_ASSERT( !result );
       }
       {
-         trace_state ts;
-         memory_input in( "a\r\n\t\0b", 6, "trace test please ignore" );
-         const auto result = parse< GRAMMAR2, trace_action, trace_control >( in, ts );
-         TAO_PEGTL_TEST_ASSERT( result );
+         memory_input in( "c", "trace test please ignore" );
+         const auto result = standard_trace< GRAMMAR4 >( in );
+         TAO_PEGTL_TEST_ASSERT( !result );
       }
    }
 
diff --git a/packages/PEGTL/src/test/pegtl/contrib_uri.cpp b/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
index a35ce90992ce71826711abb2d3131b629ec110a8..e77c3996744537c9fc4692705b359ced4de14e94 100644
--- a/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
+++ b/packages/PEGTL/src/test/pegtl/contrib_uri.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_fail.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 #include <tao/pegtl/contrib/uri.hpp>
diff --git a/packages/PEGTL/src/test/pegtl/control_unwind.cpp b/packages/PEGTL/src/test/pegtl/control_unwind.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..ad7d1251b84831c605f45256dc4b1e0f29318a8d
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/control_unwind.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <tao/pegtl.hpp>
+
+#include "test.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   struct r
+      : seq< alpha, digit >
+   {};
+
+   template< typename R >
+   struct a
+      : nothing< R >
+   {};
+
+   template< typename R >
+   struct c
+      : normal< R >
+   {};
+
+   unsigned flags = 0;
+
+   template<>
+   struct a< alpha >
+   {
+      static void apply0()
+      {
+         flags |= 1;
+      }
+   };
+
+   template<>
+   struct a< digit >
+   {
+      static void apply0()
+      {
+         throw 42;
+      }
+   };
+
+   template<>
+   struct c< r >
+      : normal< r >
+   {
+      template< typename Input >
+      static void unwind( const Input& /*unused*/ )
+      {
+         flags |= 2;
+      }
+   };
+
+   void unit_test()
+   {
+      memory_input in( "a1", __FUNCTION__ );
+      try {
+         parse< r, a, c >( in );
+         TAO_PEGTL_TEST_ASSERT( false );
+      }
+      catch( const int& e ) {
+         TAO_PEGTL_TEST_ASSERT( e == 42 );
+      }
+      TAO_PEGTL_TEST_ASSERT( flags == 3 );
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/demangle.cpp b/packages/PEGTL/src/test/pegtl/demangle.cpp
index ee76d1db381833e12ee8df9d72c3792c0c18eb21..8288a5d87ca48d9abf703692f1630b9da73bd198 100644
--- a/packages/PEGTL/src/test/pegtl/demangle.cpp
+++ b/packages/PEGTL/src/test/pegtl/demangle.cpp
@@ -3,14 +3,14 @@
 
 #include "test.hpp"
 
-#include <tao/pegtl/internal/demangle.hpp>
+#include <tao/pegtl/demangle.hpp>
 
 namespace TAO_PEGTL_NAMESPACE
 {
    template< typename T >
    void test( const std::string& s )
    {
-      TAO_PEGTL_TEST_ASSERT( internal::demangle< T >() == s );
+      TAO_PEGTL_TEST_ASSERT( demangle< T >() == s );
    }
 
    void unit_test()
@@ -23,6 +23,7 @@ namespace TAO_PEGTL_NAMESPACE
 #elif defined( _MSC_VER )
       test< int >( "int" );
       test< double >( "double" );
+      // in the Microsoft world, class and struct are not the same!
       test< seq< bytes< 42 >, eof > >( "struct tao::pegtl::seq<struct tao::pegtl::bytes<42>,struct tao::pegtl::eof>" );
 #else
       test< int >( "int" );
diff --git a/packages/PEGTL/src/test/pegtl/enable_control.cpp b/packages/PEGTL/src/test/pegtl/enable_control.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..672b4a4a932c97c6baf202d030dc5ee81aa805ae
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/enable_control.cpp
@@ -0,0 +1,70 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include <tao/pegtl.hpp>
+
+#include "test.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   struct r
+      : seq< internal::seq< any > >
+   {};
+
+   static_assert( !internal::enable_control< internal::seq< any > > );
+   static_assert( internal::enable_control< seq< any > > );
+   static_assert( !internal::enable_control< internal::seq< internal::seq< any > > > );
+   static_assert( internal::enable_control< seq< internal::seq< any > > > );
+   static_assert( internal::enable_control< r > );
+
+   static_assert( !normal< internal::seq< any > >::enable );
+   static_assert( normal< seq< any > >::enable );
+   static_assert( !normal< internal::seq< internal::seq< any > > >::enable );
+   static_assert( normal< seq< internal::seq< any > > >::enable );
+   static_assert( normal< r >::enable );
+
+   template< typename R >
+   struct a
+      : nothing< R >
+   {};
+
+   unsigned flags = 0;
+
+   template<>
+   struct a< r >
+   {
+      static void apply0()
+      {
+         flags |= 0x01;
+      }
+   };
+
+   template<>
+   struct a< any >
+   {
+      static void apply0()
+      {
+         flags |= 0x02;
+      }
+   };
+
+   template<>
+   struct a< internal::seq< any > >
+   {
+      static void apply0()
+      {
+         flags |= 0x10;
+      }
+   };
+
+   void unit_test()
+   {
+      memory_input in( "a", __FUNCTION__ );
+      const bool b = parse< r, a >( in );
+      TAO_PEGTL_TEST_ASSERT( b );
+      TAO_PEGTL_TEST_ASSERT( flags == 3 );
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/error_message.cpp b/packages/PEGTL/src/test/pegtl/error_message.cpp
index 05870afbaf6e2ff595f9eefec1e27fdd6f8543c1..ff4d1780e9d69ce85a727d1d181c98fdb9168d95 100644
--- a/packages/PEGTL/src/test/pegtl/error_message.cpp
+++ b/packages/PEGTL/src/test/pegtl/error_message.cpp
@@ -13,7 +13,7 @@ namespace test1
    struct grammar : sor< a, b > {};
 
    template< typename > inline constexpr const char* error_message = nullptr;
-   template<> inline constexpr auto error_message< test1::a > = "test123";
+   template<> inline constexpr auto error_message< test1::b > = "test123";
 
    struct error { template< typename Rule > static constexpr auto message = error_message< Rule >; };
    template< typename Rule > using control = must_if< error >::control< Rule >;
@@ -26,11 +26,11 @@ namespace TAO_PEGTL_NAMESPACE
    void unit_test()
    {
       try {
-         parse< test1::grammar, nothing, test1::control >( memory_input( "b", __FUNCTION__ ) );
+         parse< test1::grammar, nothing, test1::control >( memory_input( "c", __FUNCTION__ ) );
          TAO_PEGTL_TEST_ASSERT( false );
       }
       catch( const parse_error& e ) {
-         if( e.what() != std::string( "test123" ) ) {
+         if( e.message() != "test123" ) {
             throw;
          }
       }
diff --git a/packages/PEGTL/src/test/pegtl/file_read.cpp b/packages/PEGTL/src/test/pegtl/file_read.cpp
index 00b0184ee21b5190136da3c3334549a952f87bee..66f52fdd54e348df8d2265be8aec14dee403f51a 100644
--- a/packages/PEGTL/src/test/pegtl/file_read.cpp
+++ b/packages/PEGTL/src/test/pegtl/file_read.cpp
@@ -10,12 +10,8 @@ namespace TAO_PEGTL_NAMESPACE
    struct open_input
       : public read_input< P, Eol >
    {
-      explicit open_input( const char* in_filename )
-         : read_input< P, Eol >( internal::file_open( in_filename ), in_filename )
-      {}
-
-      explicit open_input( const std::string& in_filename )
-         : open_input( in_filename.c_str() )
+      explicit open_input( const std::filesystem::path& path )
+         : read_input< P, Eol >( internal::file_open( path ), path )
       {}
    };
 
diff --git "a/packages/PEGTL/src/test/pegtl/file_\303\244\303\266\303\274\360\235\204\236_data.txt" "b/packages/PEGTL/src/test/pegtl/file_\303\244\303\266\303\274\360\235\204\236_data.txt"
new file mode 100644
index 0000000000000000000000000000000000000000..d1c7bba09c907f77a6eb263e90b8a5d5b7873a7e
--- /dev/null
+++ "b/packages/PEGTL/src/test/pegtl/file_\303\244\303\266\303\274\360\235\204\236_data.txt"
@@ -0,0 +1,11 @@
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
+dummy content
diff --git a/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp b/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
index 567ed28a6e1a1acd846e5831f00eba31cfb008e1..99133ba8b0638b9da36e5498b38f20e4df7f20cc 100644
--- a/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
+++ b/packages/PEGTL/src/test/pegtl/internal_file_mapper.cpp
@@ -16,12 +16,21 @@ namespace TAO_PEGTL_NAMESPACE
          std::cerr << "pegtl: unit test failed for [ internal::file_mapper ]" << std::endl;
          ++failed;
       }
-      catch( const std::system_error& e ) {
+      catch( const std::system_error& ) {
       }
       catch( ... ) {
          std::cerr << "pegtl: unit test failed for [ internal::file_mapper ] with unexpected exception" << std::endl;
          ++failed;
       }
+
+      const std::string s = "dummy content\n";
+      const std::string dummy_content = s + s + s + s + s + s + s + s + s + s + s;
+
+      internal::file_mapper mapper( "src/test/pegtl/file_data.txt" );
+      TAO_PEGTL_TEST_ASSERT( !mapper.empty() );
+      TAO_PEGTL_TEST_ASSERT( mapper.size() == 154 );
+      TAO_PEGTL_TEST_ASSERT( std::string_view( mapper.data(), mapper.size() ) == dummy_content );
+      TAO_PEGTL_TEST_ASSERT( std::string_view( mapper.begin(), mapper.end() - mapper.begin() ) == dummy_content );
    }
 
 }  // namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/parse_error.cpp b/packages/PEGTL/src/test/pegtl/parse_error.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6cab22e9a71bb7982b9162cc70e4bbf880ca2af1
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/parse_error.cpp
@@ -0,0 +1,59 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include "test.hpp"
+
+#include "verify_meta.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   template< tracking_mode M >
+   void unit_test()
+   {
+      const std::string rulename{ demangle< digit >() };
+
+      memory_input< M > in( "foo\nbar bla blubb\nbaz", "test_source" );
+
+      try {
+         parse< seq< identifier, eol, identifier, one< ' ' >, must< digit > > >( in );
+      }
+      catch( const parse_error& e ) {
+         TAO_PEGTL_TEST_ASSERT( e.what() == "test_source:2:5: parse error matching " + rulename );
+
+         TAO_PEGTL_TEST_ASSERT( e.message() == "parse error matching " + rulename );
+
+         TAO_PEGTL_TEST_ASSERT( e.positions().size() == 1 );
+         const auto& p = e.positions().front();
+
+         TAO_PEGTL_TEST_ASSERT( p.byte == 8 );
+         TAO_PEGTL_TEST_ASSERT( p.line == 2 );
+         TAO_PEGTL_TEST_ASSERT( p.column == 5 );
+         TAO_PEGTL_TEST_ASSERT( p.source == "test_source" );
+
+         TAO_PEGTL_TEST_ASSERT( in.line_at( p ) == "bar bla blubb" );
+
+         position p2 = p;
+         p2.source = "foo";
+         p2.line = 42;
+         p2.column = 123;
+
+         parse_error e2 = e;
+         e2.add_position( std::move( p2 ) );
+
+         TAO_PEGTL_TEST_ASSERT( e2.what() == "foo:42:123: test_source:2:5: parse error matching " + rulename );
+         TAO_PEGTL_TEST_ASSERT( e.what() == "test_source:2:5: parse error matching " + rulename );
+
+         return;
+      }
+      TAO_PEGTL_TEST_UNREACHABLE;
+   }
+
+   void unit_test()
+   {
+      unit_test< tracking_mode::eager >();
+      unit_test< tracking_mode::lazy >();
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/position.cpp b/packages/PEGTL/src/test/pegtl/position.cpp
index 92e8e45a0d64692e4bc42ab02c1ca54df1667749..c1912679067f95fd543519135bf6b5eaeb4a7bbd 100644
--- a/packages/PEGTL/src/test/pegtl/position.cpp
+++ b/packages/PEGTL/src/test/pegtl/position.cpp
@@ -24,7 +24,7 @@ namespace TAO_PEGTL_NAMESPACE
 
       TAO_PEGTL_TEST_ASSERT( parse< Rule >( i1 ) );
       TAO_PEGTL_TEST_ASSERT( i1.line() == 2 );
-      TAO_PEGTL_TEST_ASSERT( i1.byte_in_line() == 0 );
+      TAO_PEGTL_TEST_ASSERT( i1.column() == 1 );
    }
 
    template< typename Rule, typename ParseInput = memory_input<> >
@@ -36,7 +36,7 @@ namespace TAO_PEGTL_NAMESPACE
 
       TAO_PEGTL_TEST_ASSERT( parse< Rule >( i2 ) );
       TAO_PEGTL_TEST_ASSERT( i2.line() == 1 );
-      TAO_PEGTL_TEST_ASSERT( i2.byte_in_line() == 1 );
+      TAO_PEGTL_TEST_ASSERT( i2.column() == 2 );
    }
 
    template< typename Rule, typename ParseInput = memory_input<> >
@@ -48,7 +48,7 @@ namespace TAO_PEGTL_NAMESPACE
 
       TAO_PEGTL_TEST_ASSERT( !parse< Rule >( i3 ) );
       TAO_PEGTL_TEST_ASSERT( i3.line() == 1 );
-      TAO_PEGTL_TEST_ASSERT( i3.byte_in_line() == 0 );
+      TAO_PEGTL_TEST_ASSERT( i3.column() == 1 );
    }
 
    struct outer_grammar
@@ -73,7 +73,7 @@ namespace TAO_PEGTL_NAMESPACE
          TAO_PEGTL_TEST_ASSERT( p.source == "outer" );
          TAO_PEGTL_TEST_ASSERT( p.byte == 2 );
          TAO_PEGTL_TEST_ASSERT( p.line == 1 );
-         TAO_PEGTL_TEST_ASSERT( p.byte_in_line == 2 );
+         TAO_PEGTL_TEST_ASSERT( p.column == 3 );
          memory_input in( "dFF", "inner" );
          parse_nested< inner_grammar >( oi, in );
       }
@@ -87,15 +87,15 @@ namespace TAO_PEGTL_NAMESPACE
          parse< outer_grammar, outer_action >( oi );
       }
       catch( const parse_error& e ) {
-         TAO_PEGTL_TEST_ASSERT( e.positions.size() == 2 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 0 ].source == "inner" );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 0 ].byte == 1 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 0 ].line == 1 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 0 ].byte_in_line == 1 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 1 ].source == "outer" );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 1 ].byte == 2 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 1 ].line == 1 );
-         TAO_PEGTL_TEST_ASSERT( e.positions[ 1 ].byte_in_line == 2 );
+         TAO_PEGTL_TEST_ASSERT( e.positions().size() == 2 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 0 ].source == "inner" );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 0 ].byte == 1 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 0 ].line == 1 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 0 ].column == 2 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 1 ].source == "outer" );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 1 ].byte == 2 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 1 ].line == 1 );
+         TAO_PEGTL_TEST_ASSERT( e.positions()[ 1 ].column == 3 );
       }
    }
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_action.cpp b/packages/PEGTL/src/test/pegtl/rule_action.cpp
index 6b0c1c0dc0f11c0eefc7d50b512d49837ee42ee0..2e5096da0c437c788d73ddf50d032bcb6d5b9065 100644
--- a/packages/PEGTL/src/test/pegtl/rule_action.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_action.cpp
@@ -2,6 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_apply.cpp b/packages/PEGTL/src/test/pegtl/rule_apply.cpp
index 92bf49220e739f065c782161800b5156ed46a9a6..147e27936eb67e2179a8725a5aa16585365daa50 100644
--- a/packages/PEGTL/src/test/pegtl/rule_apply.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_apply.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_apply0.cpp b/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
index 4a25a5b3922409bcaa610d1b4fd7e019a1fbe094..1a5aebe7744ae421732a66289a017599bb9c1c20 100644
--- a/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_apply0.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_at.cpp b/packages/PEGTL/src/test/pegtl/rule_at.cpp
index 3d7f6dd484ed45ca35161a6476779f30f2c61c95..0fd1626781d3ca9eb6c25ec2d31836b2fe02e7c8 100644
--- a/packages/PEGTL/src/test/pegtl/rule_at.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_at.cpp
@@ -2,7 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_bof.cpp b/packages/PEGTL/src/test/pegtl/rule_bof.cpp
index ee02311678a3825ed5eafe01ad55a3ebe8f1ecc6..996505c1645ad9bd9ec9273a6ffba2e231a67429 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bof.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bof.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_bol.cpp b/packages/PEGTL/src/test/pegtl/rule_bol.cpp
index d50a41bbedba9aa926a55784b3019fe599bd2f46..cda3de5a6ae3a24dedfef17d68582aa765db51cb 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bol.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bol.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_bytes.cpp b/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
index 9485df93ff904fbc19338ed999d033c1ee8716c0..ea20546d3b80c25fe28db9512c6c05faad23a70d 100644
--- a/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_bytes.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_control.cpp b/packages/PEGTL/src/test/pegtl/rule_control.cpp
index 7153086e24c9e34bdcbd0d5ddc56fec1e54a6fee..214e536b7010ea6f1d04dbeff3ce4e35c29e0645 100644
--- a/packages/PEGTL/src/test/pegtl/rule_control.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_control.cpp
@@ -2,6 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_disable.cpp b/packages/PEGTL/src/test/pegtl/rule_disable.cpp
index f48626d9e5be265c60b4a988cd5e66e8f7b2b9b6..f3b67b8ff55ab248327c465cb5fa1aec9d5258ed 100644
--- a/packages/PEGTL/src/test/pegtl/rule_disable.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_disable.cpp
@@ -2,6 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_discard.cpp b/packages/PEGTL/src/test/pegtl/rule_discard.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5cb4b52d3a15784cbee390b2d7695a474612cdd7
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/rule_discard.cpp
@@ -0,0 +1,27 @@
+// Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include "test.hpp"
+
+#include "verify_meta.hpp"
+#include "verify_rule.hpp"
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   void unit_test()
+   {
+      verify_meta< discard, internal::discard >();
+
+      verify_analyze< discard >( __LINE__, __FILE__, false, false );
+
+      verify_rule< discard >( __LINE__, __FILE__, "", result_type::success, 0 );
+
+      for( char i = 1; i < 127; ++i ) {
+         char t[] = { i, 0 };
+         verify_rule< discard >( __LINE__, __FILE__, std::string( t ), result_type::success, 1 );
+      }
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_enable.cpp b/packages/PEGTL/src/test/pegtl/rule_enable.cpp
index 121b18ce458da1c145df3f132f74fbb8ee3975e0..b045319e4b76b4d1185ee73ea64d321746a3fa19 100644
--- a/packages/PEGTL/src/test/pegtl/rule_enable.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_enable.cpp
@@ -2,6 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_eof.cpp b/packages/PEGTL/src/test/pegtl/rule_eof.cpp
index 2d200d564fc3b920ebe9cf120e0550fe5bd0f725..f608a1597d745b5de5fc8a2fcd35705ff1be4a89 100644
--- a/packages/PEGTL/src/test/pegtl/rule_eof.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_eof.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_failure.cpp b/packages/PEGTL/src/test/pegtl/rule_failure.cpp
index 2f116fa201fccec496bdf9a7d9deaf6a2db7288a..9beb769b89ce0d27079fffd66e323868b8fd29a5 100644
--- a/packages/PEGTL/src/test/pegtl/rule_failure.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_failure.cpp
@@ -2,8 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
 #include "verify_char.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_if_must.cpp b/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
index 51ef74024c5e9bc45e83701b1f4c8fde7e5a10c2..54297f01d46e7e63e6b6d39c3253581a0bf31c03 100644
--- a/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_if_must.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_list.cpp b/packages/PEGTL/src/test/pegtl/rule_list.cpp
index 08748154bc642f35494232acec7f526c323f3fe6..dfd789f75236b2283e54a81a0a20e90519c32c09 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_list_must.cpp b/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
index 980a4214dc75169bf3749003187aa520a8ccce77..b036d33ce36ff105065c25e90f6e60ac138f39af 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list_must.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp b/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
index 02b18f1c5d62ade3db9698d1a7c606fb46f9b779..e121f083b2e43e9c0b7c615614680c727f9188ed 100644
--- a/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_list_tail.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_minus.cpp b/packages/PEGTL/src/test/pegtl/rule_minus.cpp
index b7c3431b30d899063fb5374d4660a33d26f83a19..8c55093e00cdb4c27bb155ccb90820b7ca32a113 100644
--- a/packages/PEGTL/src/test/pegtl/rule_minus.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_minus.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_not_at.cpp b/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
index 7fe69a528d45ab2e6501c1d0f4abee690c685ff7..25dfc9648631ddecb9673b16c9b2eb170ce11f04 100644
--- a/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_not_at.cpp
@@ -2,7 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_opt.cpp
index 05507c8c559c188671408d4dd3fdb7cbc71af81c..3c300b9a59a6bb7ba5c6fb7654938a704909a87a 100644
--- a/packages/PEGTL/src/test/pegtl/rule_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_opt.cpp
@@ -2,7 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp b/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
index 783b0d994cb14c8b9bda715ce80b7f527433c5c7..e13ef58af6f34396e5444c497241c5b5d1d72873 100644
--- a/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_opt_must.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_pad.cpp b/packages/PEGTL/src/test/pegtl/rule_pad.cpp
index 5e495b7d587dcb7602a3ed8c3de4e5528e2b1714..a3151dc20a7aca37a218cf7d3b48d1a553064874 100644
--- a/packages/PEGTL/src/test/pegtl/rule_pad.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_pad.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
index 7b63fcd7566a8e8c2182ea56821f2f7ed7aea59e..bb06f3e483605f623bcbb6ef219207a5a91d29fb 100644
--- a/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_pad_opt.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_plus.cpp b/packages/PEGTL/src/test/pegtl/rule_plus.cpp
index 6882ebfced1f7765c662060b070fff3667ab24d7..6345b4a46a5cc56368d30e4eebce9bc70b16588e 100644
--- a/packages/PEGTL/src/test/pegtl/rule_plus.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_plus.cpp
@@ -2,13 +2,17 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< plus< alpha >, internal::plus< alpha >, alpha >();
+      verify_meta< plus< alpha, digit >, internal::plus< internal::seq< alpha, digit > >, internal::seq< alpha, digit > >();
+
       verify_analyze< plus< eof > >( __LINE__, __FILE__, false, true );
       verify_analyze< plus< any > >( __LINE__, __FILE__, true, false );
       verify_analyze< plus< eof, eof, eof > >( __LINE__, __FILE__, false, true );
diff --git a/packages/PEGTL/src/test/pegtl/rule_raise.cpp b/packages/PEGTL/src/test/pegtl/rule_raise.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..565df60d294c3f90d8546d53fd9018981db0d7fe
--- /dev/null
+++ b/packages/PEGTL/src/test/pegtl/rule_raise.cpp
@@ -0,0 +1,37 @@
+// Copyright (c) 2020 Dr. Colin Hirsch and Daniel Frey
+// Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
+
+#include "test.hpp"
+
+#include "verify_meta.hpp"
+
+#if defined( _MSC_VER )
+#pragma warning( push )
+#pragma warning( disable : 4702 )
+#endif
+
+namespace TAO_PEGTL_NAMESPACE
+{
+   void unit_test()
+   {
+      verify_meta< raise< int >, internal::raise< int > >();
+      verify_meta< raise< any >, internal::raise< any > >();
+
+      verify_analyze< raise< int > >( __LINE__, __FILE__, true, false );
+      verify_analyze< raise< any > >( __LINE__, __FILE__, true, false );
+
+      memory_input in( "foo", __FUNCTION__ );
+
+      TAO_PEGTL_TEST_THROWS( parse< raise< int > >( in ) );
+      TAO_PEGTL_TEST_ASSERT( in.size( 4 ) == 3 );
+      TAO_PEGTL_TEST_THROWS( parse< raise< any > >( in ) );
+      TAO_PEGTL_TEST_ASSERT( in.size( 4 ) == 3 );
+   }
+
+}  // namespace TAO_PEGTL_NAMESPACE
+
+#if defined( _MSC_VER )
+#pragma warning( pop )
+#endif
+
+#include "main.hpp"
diff --git a/packages/PEGTL/src/test/pegtl/rule_rematch.cpp b/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
index 6eec3dba03df0aa960660d090371929d39e2845e..ee467deee34864932f88d88e7c1144881fdb9011 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rematch.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep.cpp b/packages/PEGTL/src/test/pegtl/rule_rep.cpp
index d57410382c60e1940b46ea148b32990f45122fea..b4bd7f97abd7f596e0e391741824c7e111bc54a9 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
index 09b52e8587b2845cfdc5b4bba324c2675a5e48fa..db1205a68e79ba15dca8f677006d539d567ece23 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_max.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
index 73fc6ea3797ac73125756d4dd7ad9700d132f4ed..c3648b94d43c67feb8c1b71938ba0422c517689a 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_min.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
index 0833aa62c5b558f9f8bd555f272c33a3037e46cf..de847617749f3ff0d6756d6b6c415608fb92aa9e 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_min_max.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp b/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
index 4c66f6eac4070464a368a8c86b51dfeac70fb9a3..5d3cbc708b6c624d6e1301cb3ec051b353efde72 100644
--- a/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_rep_opt.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_require.cpp b/packages/PEGTL/src/test/pegtl/rule_require.cpp
index 0d2ce914743f822b7cd8cb7f31ccca0c1b02e56d..4f4206696f49641626100bc79f5721ba15cf3c34 100644
--- a/packages/PEGTL/src/test/pegtl/rule_require.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_require.cpp
@@ -2,13 +2,17 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< require< 0 >, internal::success >();
+      verify_meta< require< 1 >, internal::require< 1 > >();
+
       verify_analyze< require< 0 > >( __LINE__, __FILE__, false, false );
       verify_analyze< require< 1 > >( __LINE__, __FILE__, false, false );
       verify_analyze< require< 9 > >( __LINE__, __FILE__, false, false );
diff --git a/packages/PEGTL/src/test/pegtl/rule_seq.cpp b/packages/PEGTL/src/test/pegtl/rule_seq.cpp
index 22505494a0df37cfee9b0523ef65df5b4335d9c9..c5e3722b3d3c419e05669fdc0dfe668a2c9f281c 100644
--- a/packages/PEGTL/src/test/pegtl/rule_seq.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_seq.cpp
@@ -2,12 +2,18 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< seq<>, internal::success >();
+      verify_meta< seq< alpha >, internal::seq< alpha >, alpha >();
+      verify_meta< seq< alpha, digit >, internal::seq< alpha, digit >, alpha, digit >();
+
       verify_seqs< seq >();
    }
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_sor.cpp b/packages/PEGTL/src/test/pegtl/rule_sor.cpp
index b0792b4432c7b68b6f14ac4cbe3fa87c3aa2d69c..11eb9a623619aa5d3aa546f5a13bd9c7693bb3ce 100644
--- a/packages/PEGTL/src/test/pegtl/rule_sor.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_sor.cpp
@@ -2,13 +2,18 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< sor<>, internal::failure >();
+      verify_meta< sor< alpha >, internal::sor< alpha >, alpha >();
+      verify_meta< sor< alpha, digit >, internal::sor< alpha, digit >, alpha, digit >();
+
       verify_analyze< sor< eof > >( __LINE__, __FILE__, false, false );
       verify_analyze< sor< any > >( __LINE__, __FILE__, true, false );
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_star.cpp b/packages/PEGTL/src/test/pegtl/rule_star.cpp
index 8cfaa6ca695a69d54eac63338bac3c5dbaa93215..f870d6a4c475e721f595a29516c721d8b49198fa 100644
--- a/packages/PEGTL/src/test/pegtl/rule_star.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_star.cpp
@@ -2,13 +2,17 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< star< alpha >, internal::star< alpha >, alpha >();
+      verify_meta< star< alpha, digit >, internal::star< internal::seq< alpha, digit > >, internal::seq< alpha, digit > >();
+
       verify_analyze< star< eof > >( __LINE__, __FILE__, false, true );
       verify_analyze< star< any > >( __LINE__, __FILE__, false, false );
       verify_analyze< star< eof, eof, eof > >( __LINE__, __FILE__, false, true );
diff --git a/packages/PEGTL/src/test/pegtl/rule_star_must.cpp b/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
index e0f863a3f45860fab3940b56f250067ef9e5d26b..0a6410ad554db9e40ca1c1288bdb7ce5cc3a56ee 100644
--- a/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_star_must.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/rule_state.cpp b/packages/PEGTL/src/test/pegtl/rule_state.cpp
index 77ed17f01bb18186b64b8f6a6b2dd2ffa7647d0b..c940ac224445716b15358e941b6ea77bd15ff108 100644
--- a/packages/PEGTL/src/test/pegtl/rule_state.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_state.cpp
@@ -2,6 +2,8 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_seqs.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
@@ -22,6 +24,10 @@ namespace TAO_PEGTL_NAMESPACE
 
    void unit_test()
    {
+      verify_meta< state< test_state_state >, internal::success >();
+      verify_meta< state< test_state_state, any >, internal::state< test_state_state, any >, any >();
+      verify_meta< state< test_state_state, alpha, digit >, internal::state< test_state_state, internal::seq< alpha, digit > >, internal::seq< alpha, digit > >();
+
       verify_seqs< test_state_rule >();
    }
 
diff --git a/packages/PEGTL/src/test/pegtl/rule_success.cpp b/packages/PEGTL/src/test/pegtl/rule_success.cpp
index fcaff87acb0fd6f9562dedb90f4c7fd2ba78116e..a6917da3c701a709ca8a2eb24956ed9ec28d4870 100644
--- a/packages/PEGTL/src/test/pegtl/rule_success.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_success.cpp
@@ -2,13 +2,16 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
 {
    void unit_test()
    {
+      verify_meta< success, internal::success >();
+
       verify_analyze< success >( __LINE__, __FILE__, false, false );
 
       verify_rule< success >( __LINE__, __FILE__, "", result_type::success, 0 );
diff --git a/packages/PEGTL/src/test/pegtl/rule_until.cpp b/packages/PEGTL/src/test/pegtl/rule_until.cpp
index f1e49d3946460dcf36194298c3a3b4f07ea7128d..24ab38ef008d8ebec2cfad979862dec765925ace 100644
--- a/packages/PEGTL/src/test/pegtl/rule_until.cpp
+++ b/packages/PEGTL/src/test/pegtl/rule_until.cpp
@@ -2,7 +2,7 @@
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
 #include "test.hpp"
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/test.hpp b/packages/PEGTL/src/test/pegtl/test.hpp
index 18868e018cd60583c214a4073059bb9aaa193f71..5b017a559477a5b5fb499fdf2c6312b3f237a17c 100644
--- a/packages/PEGTL/src/test/pegtl/test.hpp
+++ b/packages/PEGTL/src/test/pegtl/test.hpp
@@ -25,7 +25,7 @@ namespace TAO_PEGTL_NAMESPACE
 #define TAO_PEGTL_TEST_FAILED( MeSSaGe )            \
    do {                                             \
       std::cerr << "pegtl: unit test failed for [ " \
-                << internal::demangle< Rule >()     \
+                << tao::demangle< Rule >()          \
                 << " ] "                            \
                 << TAO_PEGTL_TEST_UNWRAP( MeSSaGe ) \
                 << " in line [ "                    \
@@ -67,4 +67,10 @@ namespace TAO_PEGTL_NAMESPACE
       }                                             \
    } while( false )
 
+#define TAO_PEGTL_TEST_UNREACHABLE                                                                                              \
+   do {                                                                                                                         \
+      std::cerr << "Code should be unreachable in " << __FUNCTION__ << " (" << __FILE__ << ':' << __LINE__ << ')' << std::endl; \
+      std::abort();                                                                                                             \
+   } while( false )
+
 #endif
diff --git a/packages/PEGTL/src/test/pegtl/verify_file.hpp b/packages/PEGTL/src/test/pegtl/verify_file.hpp
index 7ac5683c4444ece26b41c569e5f6c831d09bbb94..2f8a16a87eb1a0d7e59b47819cbadc78b4d965e6 100644
--- a/packages/PEGTL/src/test/pegtl/verify_file.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_file.hpp
@@ -8,6 +8,12 @@
 
 #include "test.hpp"
 
+#if defined( _MSC_VER )
+#define TAO_PEGTL_TEST_FILENAME u"src/test/pegtl/file_äöü𝄞_data.txt"
+#else
+#define TAO_PEGTL_TEST_FILENAME "src/test/pegtl/file_äöü𝄞_data.txt"
+#endif
+
 namespace TAO_PEGTL_NAMESPACE
 {
    struct file_content
@@ -51,9 +57,8 @@ namespace TAO_PEGTL_NAMESPACE
    void verify_file()
    {
       {
-         const std::string f{ "src/test/pegtl/no_such_file.txt" };
          try {
-            T in( f );
+            T in( "src/test/pegtl/no_such_file.txt" );
             parse< file_grammar >( in );
             TAO_PEGTL_TEST_ASSERT( !"no error on opening non-existing file" );
          }
@@ -61,43 +66,34 @@ namespace TAO_PEGTL_NAMESPACE
          }
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( "src/test/pegtl/file_data.txt" );
          std::cout << in.source() << std::endl;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
+         TAO_PEGTL_TEST_ASSERT( in.source() == "src/test/pegtl/file_data.txt" );
          TAO_PEGTL_TEST_ASSERT( parse< file_grammar >( in ) );
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
+         TAO_PEGTL_TEST_ASSERT( in.source() == "src/test/pegtl/file_data.txt" );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = true;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          TAO_PEGTL_TEST_ASSERT( parse< file_grammar >( in, flag ) );
          TAO_PEGTL_TEST_ASSERT( flag == true );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          TAO_PEGTL_TEST_ASSERT( parse< file_grammar >( in, flag ) );
          TAO_PEGTL_TEST_ASSERT( flag == false );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          const bool result = parse< file_grammar, file_action >( in, flag );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( flag == true );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          const bool result = parse< file_grammar, nothing, file_control >( in, flag );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( flag == true );
@@ -105,42 +101,31 @@ namespace TAO_PEGTL_NAMESPACE
       const char* foo = "foo";
       const memory_input m( foo, foo + 3, foo );
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          TAO_PEGTL_TEST_ASSERT( parse_nested< file_grammar >( m, in ) );
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = true;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          TAO_PEGTL_TEST_ASSERT( parse_nested< file_grammar >( m, in, flag ) );
          TAO_PEGTL_TEST_ASSERT( flag == true );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          TAO_PEGTL_TEST_ASSERT( parse_nested< file_grammar >( m, in, flag ) );
          TAO_PEGTL_TEST_ASSERT( flag == false );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          const bool result = parse_nested< file_grammar, file_action >( m, in, flag );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( flag == true );
       }
       {
-         const std::string f{ "src/test/pegtl/file_data.txt" };
-         T in( f );
+         T in( TAO_PEGTL_TEST_FILENAME );
          bool flag = false;
-         TAO_PEGTL_TEST_ASSERT( in.source() == f );
          const bool result = parse_nested< file_grammar, nothing, file_control >( m, in, flag );
          TAO_PEGTL_TEST_ASSERT( result );
          TAO_PEGTL_TEST_ASSERT( flag == true );
diff --git a/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp b/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
index ac9368178f87a178b186d7fd68c8334a1d42c30f..0e17e8c75a95df897949e4c44301567941a9f1e4 100644
--- a/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_ifmt.hpp
@@ -6,7 +6,7 @@
 
 #include <tao/pegtl.hpp>
 
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/packages/PEGTL/src/test/pegtl/verify_impl.hpp b/packages/PEGTL/src/test/pegtl/verify_impl.hpp
index 83ad2977ca4f4d6abc0e540f594d7bbfbe026ff6..b98cf45a35af2cdcc57fa9297adad95065f1b0c1 100644
--- a/packages/PEGTL/src/test/pegtl/verify_impl.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_impl.hpp
@@ -31,8 +31,7 @@ namespace TAO_PEGTL_NAMESPACE
          return result_type::global_failure;
       }
       catch( ... ) {
-         std::cerr << "Code should be unreachable in " << __FUNCTION__ << " (" << __FILE__ << ':' << __LINE__ << ')' << std::endl;
-         std::abort();
+         TAO_PEGTL_TEST_UNREACHABLE;
       }
    }
 
diff --git a/packages/PEGTL/src/test/pegtl/verify_analyze.hpp b/packages/PEGTL/src/test/pegtl/verify_meta.hpp
similarity index 70%
rename from packages/PEGTL/src/test/pegtl/verify_analyze.hpp
rename to packages/PEGTL/src/test/pegtl/verify_meta.hpp
index befefba5b6d8eaf5046aaf672db2719719507718..9c005eb9f197a45cc7edaa1cf12592656421bbb3 100644
--- a/packages/PEGTL/src/test/pegtl/verify_analyze.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_meta.hpp
@@ -1,8 +1,12 @@
 // Copyright (c) 2014-2020 Dr. Colin Hirsch and Daniel Frey
 // Please see LICENSE for license or visit https://github.com/taocpp/PEGTL/
 
-#ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_ANALYZE_HPP
-#define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_ANALYZE_HPP
+#ifndef TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_META_HPP
+#define TAO_PEGTL_SRC_TEST_PEGTL_VERIFY_META_HPP
+
+#include <type_traits>
+
+#include <tao/pegtl/type_list.hpp>
 
 #include <tao/pegtl/contrib/analyze.hpp>
 
@@ -10,6 +14,13 @@
 
 namespace TAO_PEGTL_NAMESPACE
 {
+   template< typename Name, typename Rule, typename... Rules >
+   void verify_meta()
+   {
+      static_assert( std::is_same_v< typename Name::rule_t, Rule > );
+      static_assert( std::is_same_v< typename Name::subs_t, type_list< Rules... > > );
+   }
+
    template< typename Rule >
    void verify_analyze( const unsigned line, const char* file, const bool expect_consume, const bool expect_problems )
    {
diff --git a/packages/PEGTL/src/test/pegtl/verify_rule.hpp b/packages/PEGTL/src/test/pegtl/verify_rule.hpp
index fc614e06da2d5a0711f804544a245833de4cd1d3..4ee93a9c8bc228894736da8221f234e2514aa193 100644
--- a/packages/PEGTL/src/test/pegtl/verify_rule.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_rule.hpp
@@ -17,13 +17,6 @@
 
 namespace TAO_PEGTL_NAMESPACE
 {
-   template< typename Name, typename Rule, typename... Rules >
-   void verify_meta()
-   {
-      static_assert( std::is_same_v< typename Name::rule_t, Rule > );
-      static_assert( std::is_same_v< typename Name::subs_t, type_list< Rules... > > );
-   }
-
    template< typename Rule >
    struct verify_action_impl
    {
@@ -47,19 +40,19 @@ namespace TAO_PEGTL_NAMESPACE
          remain = ( expected == result_type::success ) ? 0 : int( data.size() );
       }
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, nothing >( line, file, data, in, expected, remain );
          memory_input< tracking_mode::lazy, Eol > i2( data.data(), data.data() + data.size(), file );
          verify_impl_one< Rule, nothing >( line, file, data, i2, expected, remain );
       }
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, verify_action_impl >( line, file, data, in, expected, remain );
          memory_input< tracking_mode::lazy, Eol > i2( data.data(), data.data() + data.size(), file );
          verify_impl_one< Rule, verify_action_impl >( line, file, data, i2, expected, remain );
       }
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, verify_action_impl0 >( line, file, data, in, expected, remain );
          memory_input< tracking_mode::lazy, Eol > i2( data.data(), data.data() + data.size(), file );
          verify_impl_one< Rule, verify_action_impl0 >( line, file, data, i2, expected, remain );
@@ -70,15 +63,15 @@ namespace TAO_PEGTL_NAMESPACE
    void verify_only( const std::size_t line, const char* file, const std::string& data, const result_type expected, const std::size_t remain )
    {
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, nothing >( line, file, data, in, expected, remain );
       }
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, verify_action_impl >( line, file, data, in, expected, remain );
       }
       {
-         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 0 );
+         memory_input< tracking_mode::eager, Eol > in( data.data(), data.data() + data.size(), file, 0, line, 1 );
          verify_impl_one< Rule, verify_action_impl0 >( line, file, data, in, expected, remain );
       }
    }
diff --git a/packages/PEGTL/src/test/pegtl/verify_seqs.hpp b/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
index 82d84b0e8bd026a56d2fb0d911f2e27e4ba10fa4..29c21681233b6682511640ee51cf5732f001f99b 100644
--- a/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
+++ b/packages/PEGTL/src/test/pegtl/verify_seqs.hpp
@@ -6,7 +6,7 @@
 
 #include <tao/pegtl.hpp>
 
-#include "verify_analyze.hpp"
+#include "verify_meta.hpp"
 #include "verify_rule.hpp"
 
 namespace TAO_PEGTL_NAMESPACE
diff --git a/src/language/PEGGrammar.hpp b/src/language/PEGGrammar.hpp
index 336fb5583fb4e163f37feb30956acbf27cef36ab..61ba912a567c4a6d868d22cc7915e7557f484518 100644
--- a/src/language/PEGGrammar.hpp
+++ b/src/language/PEGGrammar.hpp
@@ -3,6 +3,8 @@
 
 #include <pegtl.hpp>
 
+#include <language/utils/ParseError.hpp>
+
 using namespace TAO_PEGTL_NAMESPACE;
 
 namespace language
@@ -325,12 +327,12 @@ struct errors : public normal<Rule>
   static void
   raise(const Input& in, States&&... /*unused*/)
   {
-    throw parse_error(error_message, std::vector{in.position()});
+    throw ParseError(error_message, std::vector{in.position()});
   }
 };
 
 template <typename Rule>
-inline const std::string errors<Rule>::error_message = "parse error matching "+ demangle(internal::demangle< Rule >());
+inline const std::string errors<Rule>::error_message = "parse error matching "+ demangle(tao::demangle< Rule >());
 
 template <>
 inline const std::string errors<language::module_name>::error_message = "parse error, missing module name";
diff --git a/src/language/PugsParser.cpp b/src/language/PugsParser.cpp
index 235eab368dccfd2c41dde312a6f24264388a7813..75842ad029795a579a213b2d50bed15bcfb68b30 100644
--- a/src/language/PugsParser.cpp
+++ b/src/language/PugsParser.cpp
@@ -90,14 +90,14 @@ parser(const std::string& filename)
     try {
       parse_and_execute(input);
     }
-    catch (const parse_error& e) {
-      const auto p = e.positions.front();
+    catch (const ParseError& e) {
+      const auto p = e.positions().front();
 
-      std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.byte_in_line << ": " << rang::style::reset
+      std::cerr << rang::style::bold << p.source << ':' << p.line << ':' << p.column << ": " << rang::style::reset
                 << rang::fgB::red << "error: " << rang::fg::reset << rang::style::bold << e.what() << rang::style::reset
                 << '\n'
                 << input.line_at(p) << '\n'
-                << std::string(p.byte_in_line, ' ') << rang::fgB::yellow << '^' << rang::fg::reset << '\n';
+                << std::string(p.column, ' ') << rang::fgB::yellow << '^' << rang::fg::reset << '\n';
       finalize();
       std::exit(1);
     }
diff --git a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
index 43bd40932c82310d039f851c9ddc810744c6f72b..e27bd531d743b410ecb381e665bb4af145e4731f 100644
--- a/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeAffectationExpressionBuilder.cpp
@@ -4,6 +4,7 @@
 #include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/node_processor/AffectationProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 #include <utils/Exceptions.hpp>
 
@@ -33,8 +34,8 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: undefined operand type for affectation",
-                          std::vector{n.children[1]->begin()});
+        throw ParseError("unexpected error: undefined operand type for affectation",
+                         std::vector{n.children[1]->begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -83,7 +84,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
             }
           }
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid integral value", std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: invalid integral value", std::vector{n.children[1]->begin()});
           // LCOV_EXCL_STOP
         }
         case ASTNodeDataType::double_t: {
@@ -94,7 +95,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: invalid operand type", std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: invalid operand type", std::vector{n.children[1]->begin()});
         }
           // LCOV_EXCL_STOP
         }
@@ -107,7 +108,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: invalid operand type", std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: invalid operand type", std::vector{n.children[1]->begin()});
         }
           // LCOV_EXCL_STOP
         }
@@ -130,11 +131,11 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
           break;
         }
         default: {
-          throw parse_error("expecting scalar operand type", std::vector{n.children[1]->begin()});
+          throw ParseError("expecting scalar operand type", std::vector{n.children[1]->begin()});
         }
         }
       } else {
-        throw parse_error("invalid affectation operator for " + dataTypeName(n.m_data_type), std::vector{n.begin()});
+        throw ParseError("invalid affectation operator for " + dataTypeName(n.m_data_type), std::vector{n.begin()});
       }
     };
 
@@ -179,8 +180,8 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
           }
             // LCOV_EXCL_START
           default: {
-            throw parse_error("unexpected error: invalid vector dimension for string affectation",
-                              std::vector{n.children[1]->begin()});
+            throw ParseError("unexpected error: invalid vector dimension for string affectation",
+                             std::vector{n.children[1]->begin()});
           }
             // LCOV_EXCL_STOP
           }
@@ -188,13 +189,13 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: undefined operand type for string affectation",
-                            std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: undefined operand type for string affectation",
+                           std::vector{n.children[1]->begin()});
         }
           // LCOV_EXCL_STOP
         }
       } else {
-        throw parse_error("invalid affectation operator for string", std::vector{n.begin()});
+        throw ParseError("invalid affectation operator for string", std::vector{n.begin()});
       }
     };
 
@@ -209,14 +210,14 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: undefined operand type for embedded data affectation",
-                            std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: undefined operand type for embedded data affectation",
+                           std::vector{n.children[1]->begin()});
         }
           // LCOV_EXCL_STOP
         }
       } else {
-        throw parse_error("invalid affectation operator for '" + dataTypeName(n.children[0]->m_data_type) + "'",
-                          std::vector{n.begin()});
+        throw ParseError("invalid affectation operator for '" + dataTypeName(n.children[0]->m_data_type) + "'",
+                         std::vector{n.begin()});
       }
     };
 
@@ -266,8 +267,8 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
             }
               // LCOV_EXCL_START
             default: {
-              throw parse_error("unexpected error: invalid vector dimension for tuple affectation",
-                                std::vector{n.children[1]->begin()});
+              throw ParseError("unexpected error: invalid vector dimension for tuple affectation",
+                               std::vector{n.children[1]->begin()});
             }
               // LCOV_EXCL_STOP
             }
@@ -321,8 +322,8 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
             }
               // LCOV_EXCL_START
             default: {
-              throw parse_error("unexpected error: invalid vector dimension for tuple affectation",
-                                std::vector{n.children[1]->begin()});
+              throw ParseError("unexpected error: invalid vector dimension for tuple affectation",
+                               std::vector{n.children[1]->begin()});
             }
               // LCOV_EXCL_STOP
             }
@@ -330,15 +331,15 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
           }
           // LCOV_EXCL_START
           default: {
-            throw parse_error("unexpected error: undefined operand type for tuple affectation",
-                              std::vector{n.children[1]->begin()});
+            throw ParseError("unexpected error: undefined operand type for tuple affectation",
+                             std::vector{n.children[1]->begin()});
           }
             // LCOV_EXCL_STOP
           }
         }
       } else {
-        throw parse_error("invalid affectation operator for '" + dataTypeName(n.children[0]->m_data_type) + "'",
-                          std::vector{n.begin()});
+        throw ParseError("invalid affectation operator for '" + dataTypeName(n.children[0]->m_data_type) + "'",
+                         std::vector{n.begin()});
       }
     };
 
@@ -378,7 +379,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: unexpected vector dimension", std::vector{n.begin()});
+          throw ParseError("unexpected error: unexpected vector dimension", std::vector{n.begin()});
         }
           // LCOV_EXCL_STOP
         }
@@ -398,8 +399,7 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
         break;
       }
       default: {
-        throw parse_error("unexpected error: undefined value type for affectation",
-                          std::vector{n.children[0]->begin()});
+        throw ParseError("unexpected error: undefined value type for affectation", std::vector{n.children[0]->begin()});
       }
       }
     };
@@ -430,6 +430,6 @@ ASTNodeAffectationExpressionBuilder::ASTNodeAffectationExpressionBuilder(ASTNode
   } else if (n.is_type<language::minuseq_op>()) {
     set_affectation_processor(n, language::minuseq_op{});
   } else {
-    throw parse_error("unexpected error: undefined affectation operator", std::vector{n.begin()});
+    throw ParseError("unexpected error: undefined affectation operator", std::vector{n.begin()});
   }
 }
diff --git a/src/language/ast/ASTNodeArraySubscriptExpressionBuilder.cpp b/src/language/ast/ASTNodeArraySubscriptExpressionBuilder.cpp
index e50b84668b6cb11d900554002ade3d6ba2937608..350a4070e9a6bc65906627d54b2e2ed87c403cc6 100644
--- a/src/language/ast/ASTNodeArraySubscriptExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeArraySubscriptExpressionBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <algebra/TinyVector.hpp>
 #include <language/node_processor/ArraySubscriptProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 ASTNodeArraySubscriptExpressionBuilder::ASTNodeArraySubscriptExpressionBuilder(ASTNode& node)
 {
@@ -22,11 +23,11 @@ ASTNodeArraySubscriptExpressionBuilder::ASTNodeArraySubscriptExpressionBuilder(A
       break;
     }
     default: {
-      throw parse_error("unexpected error: invalid array dimension", array_expression.begin());
+      throw ParseError("unexpected error: invalid array dimension", array_expression.begin());
       break;
     }
     }
   } else {
-    throw parse_error("unexpected error: invalid array type", array_expression.begin());
+    throw ParseError("unexpected error: invalid array type", array_expression.begin());
   }
 }
diff --git a/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp b/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
index 92074474abd0b1109feee7a53537deac2d4e44d4..64ff14deb191921c81e28ef1b3f36a0651ffc0e2 100644
--- a/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeBinaryOperatorExpressionBuilder.cpp
@@ -3,6 +3,7 @@
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/BinaryExpressionProcessor.hpp>
 #include <language/node_processor/ConcatExpressionProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(ASTNode& n)
 {
@@ -35,7 +36,7 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
             break;
           }
           default: {
-            throw parse_error("undefined operand type for binary operator", std::vector{n.children[1]->begin()});
+            throw ParseError("undefined operand type for binary operator", std::vector{n.children[1]->begin()});
           }
           }
 
@@ -44,10 +45,10 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
           if (data_type_b == ASTNodeDataType::string_t) {
             n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, DataTA, std::string>>(n);
           } else {
-            throw parse_error("undefined operand type for binary operator", std::vector{n.begin()});
+            throw ParseError("undefined operand type for binary operator", std::vector{n.begin()});
           }
         } else {
-          throw parse_error("undefined operand type for binary operator", std::vector{n.begin()});
+          throw ParseError("undefined operand type for binary operator", std::vector{n.begin()});
         }
       } else if constexpr (std::is_same_v<DataTA, TinyVector<1>> or std::is_same_v<DataTA, TinyVector<2>> or
                            std::is_same_v<DataTA, TinyVector<3>>) {
@@ -58,15 +59,15 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
             if (data_a.dimension() == data_type_b.dimension()) {
               n.m_node_processor = std::make_unique<BinaryExpressionProcessor<OperatorT, DataTA, DataTA>>(n);
             } else {
-              throw parse_error("incompatible dimensions of operands", std::vector{n.begin()});
+              throw ParseError("incompatible dimensions of operands", std::vector{n.begin()});
             }
           } else {
-            throw parse_error("invalid binary operator", std::vector{n.begin()});
+            throw ParseError("invalid binary operator", std::vector{n.begin()});
           }
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid operand type for binary operator",
-                            std::vector{n.children[1]->begin()});
+          throw ParseError("unexpected error: invalid operand type for binary operator",
+                           std::vector{n.children[1]->begin()});
           // LCOV_EXCL_STOP
         }
       } else {
@@ -104,7 +105,7 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
             }
               // LCOV_EXCL_START
             default: {
-              throw parse_error("unexpected error: invalid dimension", std::vector{n.children[0]->begin()});
+              throw ParseError("unexpected error: invalid dimension", std::vector{n.children[0]->begin()});
             }
               // LCOV_EXCL_STOP
             }
@@ -112,7 +113,7 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
           }
         }
         default: {
-          throw parse_error("undefined operand type for binary operator", std::vector{n.children[1]->begin()});
+          throw ParseError("undefined operand type for binary operator", std::vector{n.children[1]->begin()});
         }
         }
       }
@@ -157,14 +158,14 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: invalid dimension", std::vector{n.children[0]->begin()});
+          throw ParseError("unexpected error: invalid dimension", std::vector{n.children[0]->begin()});
         }
           // LCOV_EXCL_STOP
         }
         break;
       }
       default: {
-        throw parse_error("undefined operand type for binary operator", std::vector{n.children[0]->begin()});
+        throw ParseError("undefined operand type for binary operator", std::vector{n.children[0]->begin()});
       }
       }
     };
@@ -201,6 +202,6 @@ ASTNodeBinaryOperatorExpressionBuilder::ASTNodeBinaryOperatorExpressionBuilder(A
   } else if (n.is_type<language::not_eq_op>()) {
     set_binary_operator_processor(n, language::not_eq_op{});
   } else {
-    throw parse_error("unexpected error: undefined binary operator", std::vector{n.begin()});
+    throw ParseError("unexpected error: undefined binary operator", std::vector{n.begin()});
   }
 }
diff --git a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
index 9d914d7ee4799c9908253ffaa2958180a9d67bd3..6dc726f555219b855e2a125b0ab22b586dec2dad 100644
--- a/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeBuiltinFunctionExpressionBuilder.cpp
@@ -4,6 +4,7 @@
 #include <language/ast/ASTNodeDataTypeFlattener.hpp>
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/node_processor/BuiltinFunctionProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 
 PUGS_INLINE std::unique_ptr<IFunctionArgumentConverter>
@@ -29,8 +30,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid argument type for function",
-                        std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: invalid argument type for function",
+                       std::vector{argument_node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -47,8 +48,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
           return std::make_unique<FunctionTinyVectorArgumentConverter<ParameterT, ParameterT>>(argument_number);
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid argument dimension",
-                            std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: invalid argument dimension",
+                           std::vector{argument_node_sub_data_type.m_parent_node.begin()});
           // LCOV_EXCL_STOP
         }
       }
@@ -66,8 +67,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid argument type",
-                          std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error: invalid argument type",
+                         std::vector{argument_node_sub_data_type.m_parent_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -78,8 +79,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
           return std::make_unique<FunctionTinyVectorArgumentConverter<ParameterT, ParameterT>>(argument_number);
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid argument dimension",
-                            std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: invalid argument dimension",
+                           std::vector{argument_node_sub_data_type.m_parent_node.begin()});
           // LCOV_EXCL_STOP
         }
       }
@@ -88,8 +89,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
           return std::make_unique<FunctionTinyVectorArgumentConverter<ParameterT, ParameterT>>(argument_number);
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid argument dimension",
-                            std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: invalid argument dimension",
+                           std::vector{argument_node_sub_data_type.m_parent_node.begin()});
           // LCOV_EXCL_STOP
         }
       }
@@ -103,8 +104,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid argument type",
-                          std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error: invalid argument type",
+                         std::vector{argument_node_sub_data_type.m_parent_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -122,8 +123,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid argument type for function",
-                        std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: invalid argument type for function",
+                       std::vector{argument_node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -213,8 +214,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid argument type for function",
-                        std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: invalid argument type for function",
+                       std::vector{argument_node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -247,8 +248,8 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: undefined parameter type for function",
-                          std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error: undefined parameter type for function",
+                         std::vector{argument_node_sub_data_type.m_parent_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -286,9 +287,9 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
         }
         // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: unexpected tuple content for function: '" +
-                              dataTypeName(parameter_type) + "'",
-                            std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: unexpected tuple content for function: '" + dataTypeName(parameter_type) +
+                             "'",
+                           std::vector{argument_node_sub_data_type.m_parent_node.begin()});
         }
           // LCOV_EXCL_STOP
         }
@@ -298,16 +299,16 @@ ASTNodeBuiltinFunctionExpressionBuilder::_getArgumentConverter(const ASTNodeData
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: unexpected tuple content type for function",
-                          std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error: unexpected tuple content type for function",
+                         std::vector{argument_node_sub_data_type.m_parent_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined parameter type for function",
-                        std::vector{argument_node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: undefined parameter type for function",
+                       std::vector{argument_node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -356,7 +357,7 @@ ASTNodeBuiltinFunctionExpressionBuilder::_buildArgumentProcessors(
     std::ostringstream error_message;
     error_message << "bad number of arguments: expecting " << rang::fgB::yellow << parameters_number
                   << rang::style::reset << ", provided " << rang::fgB::yellow << arguments_number << rang::style::reset;
-    throw parse_error(error_message.str(), argument_nodes.begin());
+    throw ParseError(error_message.str(), argument_nodes.begin());
   }
 
   for (size_t i = 0; i < arguments_number; ++i) {
diff --git a/src/language/ast/ASTNodeDataType.cpp b/src/language/ast/ASTNodeDataType.cpp
index 18b812c0e7bb7c62466a7bce6b1dfd279eacec43..c77659d983c58a571ac3dd8426437be6478ceddb 100644
--- a/src/language/ast/ASTNodeDataType.cpp
+++ b/src/language/ast/ASTNodeDataType.cpp
@@ -2,17 +2,18 @@
 
 #include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNode.hpp>
+#include <language/utils/ParseError.hpp>
 #include <utils/PugsAssert.hpp>
 
 ASTNodeDataType
 getVectorDataType(const ASTNode& type_node)
 {
   if (not(type_node.is_type<language::vector_type>() and (type_node.children.size() == 2))) {
-    throw parse_error("unexpected node type", type_node.begin());
+    throw ParseError("unexpected node type", type_node.begin());
   }
   ASTNode& dimension_node = *type_node.children[1];
   if (not dimension_node.is_type<language::integer>()) {
-    throw parse_error("unexpected non integer constant dimension", dimension_node.begin());
+    throw ParseError("unexpected non integer constant dimension", dimension_node.begin());
   }
   const size_t dimension = std::stol(dimension_node.string());
   return ASTNodeDataType{ASTNodeDataType::vector_t, dimension};
diff --git a/src/language/ast/ASTNodeDataTypeBuilder.cpp b/src/language/ast/ASTNodeDataTypeBuilder.cpp
index 96ff62bfc20cd9a6526f140d14a4cee7218e241d..7da94cc5f7d00132b226f8ba0e7e5d95d4097b35 100644
--- a/src/language/ast/ASTNodeDataTypeBuilder.cpp
+++ b/src/language/ast/ASTNodeDataTypeBuilder.cpp
@@ -3,6 +3,7 @@
 #include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <utils/PugsAssert.hpp>
 
@@ -17,7 +18,7 @@ ASTNodeDataTypeBuilder::_buildDeclarationNodeDataTypes(ASTNode& type_node, ASTNo
               << type_node.string() << rang::style::reset << rang::style::bold << " differs from number of variables ("
               << name_node.children.size() << ") " << rang::fgB::yellow << name_node.string() << rang::style::reset
               << std::ends;
-      throw parse_error(message.str(), name_node.begin());
+      throw ParseError(message.str(), name_node.begin());
     }
 
     for (size_t i = 0; i < type_node.children.size(); ++i) {
@@ -47,12 +48,12 @@ ASTNodeDataTypeBuilder::_buildDeclarationNodeDataTypes(ASTNode& type_node, ASTNo
 
         const auto [i_type_symbol, found] = symbol_table.find(type_name_id, content_node->begin());
         if (not found) {
-          throw parse_error("undefined type identifier", std::vector{content_node->begin()});
+          throw ParseError("undefined type identifier", std::vector{content_node->begin()});
         } else if (i_type_symbol->attributes().dataType() != ASTNodeDataType::type_name_id_t) {
           std::ostringstream os;
           os << "invalid type identifier, '" << type_name_id << "' was previously defined as a '"
              << dataTypeName(i_type_symbol->attributes().dataType()) << "'" << std::ends;
-          throw parse_error(os.str(), std::vector{content_node->begin()});
+          throw ParseError(os.str(), std::vector{content_node->begin()});
         }
 
         content_node->m_data_type = ASTNodeDataType{ASTNodeDataType::type_id_t, type_name_id};
@@ -84,19 +85,19 @@ ASTNodeDataTypeBuilder::_buildDeclarationNodeDataTypes(ASTNode& type_node, ASTNo
 
       auto [i_type_symbol, found] = symbol_table.find(type_name_id, type_node.begin());
       if (not found) {
-        throw parse_error("undefined type identifier", std::vector{type_node.begin()});
+        throw ParseError("undefined type identifier", std::vector{type_node.begin()});
       } else if (i_type_symbol->attributes().dataType() != ASTNodeDataType::type_name_id_t) {
         std::ostringstream os;
         os << "invalid type identifier, '" << type_name_id << "' was previously defined as a '"
            << dataTypeName(i_type_symbol->attributes().dataType()) << "'" << std::ends;
-        throw parse_error(os.str(), std::vector{type_node.begin()});
+        throw ParseError(os.str(), std::vector{type_node.begin()});
       }
 
       data_type = ASTNodeDataType{ASTNodeDataType::type_id_t, type_name_id};
     }
 
     if (name_node.is_type<language::name_list>()) {
-      throw parse_error("unexpected variable list for single space", std::vector{name_node.begin()});
+      throw ParseError("unexpected variable list for single space", std::vector{name_node.begin()});
     }
 
     Assert(name_node.is_type<language::name>());
@@ -196,7 +197,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
                   << parameters_domain_node.string() << rang::style::reset << rang::style::bold
                   << " differs from number of variables (" << nb_parameter_names << ") " << rang::fgB::yellow
                   << parameters_name_node.string() << rang::style::reset << std::ends;
-          throw parse_error(message.str(), parameters_domain_node.begin());
+          throw ParseError(message.str(), parameters_domain_node.begin());
         }
 
         auto simple_type_allocator = [&](const ASTNode& type_node, ASTNode& symbol_node) {
@@ -218,7 +219,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
 
           // LCOV_EXCL_START
           if (data_type == ASTNodeDataType::undefined_t) {
-            throw parse_error("invalid parameter type", type_node.begin());
+            throw ParseError("invalid parameter type", type_node.begin());
           }
           // LCOV_EXCL_STOP
 
@@ -270,7 +271,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
               message << "expecting " << image_type.dimension() << " scalar expressions or an "
                       << dataTypeName(image_type) << ", found " << nb_image_expressions << " scalar expressions"
                       << std::ends;
-              throw parse_error(message.str(), image_domain_node.begin());
+              throw ParseError(message.str(), image_domain_node.begin());
             }
           } else {
             std::ostringstream message;
@@ -278,7 +279,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
                     << image_domain_node.string() << rang::style::reset << rang::style::bold
                     << " differs from number of expressions (" << nb_image_expressions << ") " << rang::fgB::yellow
                     << image_expression_node.string() << rang::style::reset << std::ends;
-            throw parse_error(message.str(), image_domain_node.begin());
+            throw ParseError(message.str(), image_domain_node.begin());
           }
         }
 
@@ -300,7 +301,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
 
           // LCOV_EXCL_START
           if (value_type == ASTNodeDataType::undefined_t) {
-            throw parse_error("invalid value type", image_node.begin());
+            throw ParseError("invalid value type", image_node.begin());
           }
           // LCOV_EXCL_STOP
         };
@@ -399,7 +400,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         message << "undefined binary operator\n"
                 << "note: incompatible operand types " << n.children[0]->string() << " (" << dataTypeName(type_0)
                 << ") and " << n.children[1]->string() << " (" << dataTypeName(type_1) << ')' << std::ends;
-        throw parse_error(message.str(), n.begin());
+        throw ParseError(message.str(), n.begin());
       }
     } else if (n.is_type<language::function_evaluation>()) {
       if (n.children[0]->m_data_type == ASTNodeDataType::function_t) {
@@ -452,7 +453,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
         message << "invalid function call\n"
                 << "note: '" << n.children[0]->string() << "' (type: " << dataTypeName(n.children[0]->m_data_type)
                 << ") is not a function!" << std::ends;
-        throw parse_error(message.str(), n.begin());
+        throw ParseError(message.str(), n.begin());
       }
     } else if (n.is_type<language::subscript_expression>()) {
       Assert(n.children.size() == 2, "invalid number of sub-expressions in array subscript expression");
@@ -466,7 +467,7 @@ ASTNodeDataTypeBuilder::_buildNodeDataTypes(ASTNode& n) const
                 << rang::style::reset << '[' << dataTypeName(index_expression.m_data_type) << ']'
                 << "' for array subscript" << std::ends;
 
-        throw parse_error(message.str(), n.begin());
+        throw ParseError(message.str(), n.begin());
       } else {
         n.m_data_type = ASTNodeDataType::double_t;
       }
diff --git a/src/language/ast/ASTNodeDataTypeChecker.cpp b/src/language/ast/ASTNodeDataTypeChecker.cpp
index 87ecba522b17666aa5ce1610f1caf54ce0db15e3..3e6774849a5bbef6386f606a1bfe8a6993a84192 100644
--- a/src/language/ast/ASTNodeDataTypeChecker.cpp
+++ b/src/language/ast/ASTNodeDataTypeChecker.cpp
@@ -1,10 +1,12 @@
 #include <language/ast/ASTNodeDataTypeChecker.hpp>
 
+#include <language/utils/ParseError.hpp>
+
 void
 ASTNodeDataTypeChecker::_checkNodeDataTypes(const ASTNode& n)
 {
   if (n.m_data_type == ASTNodeDataType::undefined_t) {
-    throw parse_error("unexpected error: undefined datatype for AST node for " + n.name(), n.begin());
+    throw ParseError("unexpected error: undefined datatype for AST node for " + n.name(), n.begin());
   }
 
   for (const auto& child : n.children) {
diff --git a/src/language/ast/ASTNodeDataTypeFlattener.cpp b/src/language/ast/ASTNodeDataTypeFlattener.cpp
index f19ed6667be11f429e9ab08e24ea33e264323cdb..da2d064c6dd677e9eae4444cde59700c3a61ec2d 100644
--- a/src/language/ast/ASTNodeDataTypeFlattener.cpp
+++ b/src/language/ast/ASTNodeDataTypeFlattener.cpp
@@ -51,7 +51,7 @@ ASTNodeDataTypeFlattener::ASTNodeDataTypeFlattener(ASTNode& node, FlattenedDataT
       }
         //    LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected function type", node.begin());
+        throw ParseError("unexpected function type", node.begin());
       }
         //    LCOV_EXCL_STOP
       }
diff --git a/src/language/ast/ASTNodeExpressionBuilder.cpp b/src/language/ast/ASTNodeExpressionBuilder.cpp
index 9b9a0efbcb206b164ad31d6db18b3ca3727e09f9..fcdc4e1b93ab9135a6942e0e338144dff9474116 100644
--- a/src/language/ast/ASTNodeExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeExpressionBuilder.cpp
@@ -1,5 +1,6 @@
 #include <language/ast/ASTNodeExpressionBuilder.hpp>
 
+#include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNodeAffectationExpressionBuilder.hpp>
 #include <language/ast/ASTNodeArraySubscriptExpressionBuilder.hpp>
 #include <language/ast/ASTNodeBinaryOperatorExpressionBuilder.hpp>
@@ -22,8 +23,7 @@
 #include <language/node_processor/TupleToVectorProcessor.hpp>
 #include <language/node_processor/ValueProcessor.hpp>
 #include <language/node_processor/WhileProcessor.hpp>
-
-#include <language/PEGGrammar.hpp>
+#include <language/utils/ParseError.hpp>
 
 void
 ASTNodeExpressionBuilder::_buildExpression(ASTNode& n)
@@ -121,7 +121,7 @@ ASTNodeExpressionBuilder::_buildExpression(ASTNode& n)
   } else {
     std::ostringstream error_message;
     error_message << "undefined node processor type '" << rang::fgB::red << n.name() << rang::fg::reset << "'";
-    throw parse_error{error_message.str(), std::vector{n.begin()}};
+    throw ParseError{error_message.str(), std::vector{n.begin()}};
   }
 
   for (auto& child : n.children) {
diff --git a/src/language/ast/ASTNodeFunctionEvaluationExpressionBuilder.cpp b/src/language/ast/ASTNodeFunctionEvaluationExpressionBuilder.cpp
index 5eb2ed3b0defeb35d47cd6f8e6812f2f82319883..13af5a812bd46ae0a6309eb65c1eecdda2cb027e 100644
--- a/src/language/ast/ASTNodeFunctionEvaluationExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeFunctionEvaluationExpressionBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <language/ast/ASTNodeBuiltinFunctionExpressionBuilder.hpp>
 #include <language/ast/ASTNodeFunctionExpressionBuilder.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 
 ASTNodeFunctionEvaluationExpressionBuilder::ASTNodeFunctionEvaluationExpressionBuilder(ASTNode& node)
@@ -20,7 +21,7 @@ ASTNodeFunctionEvaluationExpressionBuilder::ASTNodeFunctionEvaluationExpressionB
   }
     //    LCOV_EXCL_START
   default: {
-    throw parse_error("unexpected function type", node.begin());
+    throw ParseError("unexpected function type", node.begin());
   }
     //    LCOV_EXCL_STOP
   }
diff --git a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
index f2369940873394a33db2718a7ff42fe4870cffc2..072a2af6015eed0dab37468c0c5e7211ec887eff 100644
--- a/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeFunctionExpressionBuilder.cpp
@@ -35,7 +35,7 @@ ASTNodeFunctionExpressionBuilder::_getArgumentConverter(SymbolType& parameter_sy
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid argument type 0",
+      throw ParseError("unexpected error: invalid argument type 0",
                         std::vector{node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
@@ -51,7 +51,7 @@ ASTNodeFunctionExpressionBuilder::_getArgumentConverter(SymbolType& parameter_sy
         return std::make_unique<FunctionTinyVectorArgumentConverter<ParameterT, ParameterT>>(parameter_id);
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid argument dimension",
+        throw ParseError("unexpected error: invalid argument dimension",
                           std::vector{node_sub_data_type.m_parent_node.begin()});
         // LCOV_EXCL_STOP
       }
@@ -61,7 +61,7 @@ ASTNodeFunctionExpressionBuilder::_getArgumentConverter(SymbolType& parameter_sy
         return std::make_unique<FunctionTinyVectorArgumentConverter<ParameterT, ParameterT>>(parameter_id);
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid argument dimension",
+        throw ParseError("unexpected error: invalid argument dimension",
                           std::vector{node_sub_data_type.m_parent_node.begin()});
         // LCOV_EXCL_STOP
       }
@@ -76,7 +76,7 @@ ASTNodeFunctionExpressionBuilder::_getArgumentConverter(SymbolType& parameter_sy
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid argument type",
+      throw ParseError("unexpected error: invalid argument type",
                         std::vector{node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
@@ -121,7 +121,7 @@ ASTNodeFunctionExpressionBuilder::_getArgumentConverter(SymbolType& parameter_sy
 
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined parameter type", std::vector{m_node.begin()});
+      throw ParseError("unexpected error: undefined parameter type", std::vector{m_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -171,7 +171,7 @@ ASTNodeFunctionExpressionBuilder::_buildArgumentConverter(FunctionDescriptor& fu
     std::ostringstream error_message;
     error_message << "bad number of arguments: expecting " << rang::fgB::yellow << parameters_number
                   << rang::style::reset << ", provided " << rang::fgB::yellow << arguments_number << rang::style::reset;
-    throw parse_error(error_message.str(), argument_nodes.begin());
+    throw ParseError(error_message.str(), argument_nodes.begin());
   }
 
   if (arguments_number > 1) {
@@ -211,13 +211,13 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
         return std::make_unique<FunctionExpressionProcessor<ReturnT, std::string>>(function_component_expression);
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid string conversion", std::vector{node.children[1]->begin()});
+        throw ParseError("unexpected error: invalid string conversion", std::vector{node.children[1]->begin()});
         // LCOV_EXCL_STOP
       }
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined expression value type for function",
+      throw ParseError("unexpected error: undefined expression value type for function",
                         std::vector{node.children[1]->begin()});
     }
       // LCOV_EXCL_STOP
@@ -232,7 +232,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
         return std::make_unique<FunctionExpressionProcessor<ReturnT, ReturnT>>(function_component_expression);
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid dimension for returned vector",
+        throw ParseError("unexpected error: invalid dimension for returned vector",
                           std::vector{function_component_expression.begin()});
         // LCOV_EXCL_STOP
       }
@@ -243,7 +243,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
           function_component_expression);
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid dimension for returned vector",
+        throw ParseError("unexpected error: invalid dimension for returned vector",
                           std::vector{function_component_expression.begin()});
         // LCOV_EXCL_STOP
       }
@@ -255,13 +255,13 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
         }
       }
       // LCOV_EXCL_START
-      throw parse_error("unexpected error: undefined expression value type for function",
+      throw ParseError("unexpected error: undefined expression value type for function",
                         std::vector{function_component_expression.begin()});
       // LCOV_EXCL_STOP
     }
     // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined expression value type for function",
+      throw ParseError("unexpected error: undefined expression value type for function",
                         std::vector{function_component_expression.begin()});
     }
       // LCOV_EXCL_STOP
@@ -299,7 +299,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid dimension in returned type", std::vector{node.begin()});
+        throw ParseError("unexpected error: invalid dimension in returned type", std::vector{node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -309,7 +309,7 @@ ASTNodeFunctionExpressionBuilder::_getFunctionProcessor(const ASTNodeDataType& r
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined return type for function", std::vector{node.begin()});
+      throw ParseError("unexpected error: undefined return type for function", std::vector{node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -393,7 +393,7 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid vector_t dimension", std::vector{node.begin()});
+        throw ParseError("unexpected error: invalid vector_t dimension", std::vector{node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -417,13 +417,13 @@ ASTNodeFunctionExpressionBuilder::ASTNodeFunctionExpressionBuilder(ASTNode& node
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: invalid vector_t dimension", std::vector{node.begin()});
+          throw ParseError("unexpected error: invalid vector_t dimension", std::vector{node.begin()});
         }
           // LCOV_EXCL_STOP
         }
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: expecting 0", std::vector{function_expression.begin()});
+        throw ParseError("unexpected error: expecting 0", std::vector{function_expression.begin()});
         // LCOV_EXCL_STOP
       }
     } else {
diff --git a/src/language/ast/ASTNodeIncDecExpressionBuilder.cpp b/src/language/ast/ASTNodeIncDecExpressionBuilder.cpp
index c5eccdfcf61c597f97be00afcb688e3da857a220..71e5c3c4b16d8535091b98cab298ff6624661508 100644
--- a/src/language/ast/ASTNodeIncDecExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeIncDecExpressionBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/IncDecExpressionProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 ASTNodeIncDecExpressionBuilder::ASTNodeIncDecExpressionBuilder(ASTNode& n)
 {
@@ -22,7 +23,7 @@ ASTNodeIncDecExpressionBuilder::ASTNodeIncDecExpressionBuilder(ASTNode& n)
         break;
       }
       default: {
-        throw parse_error("unexpected error: undefined data type for unary operator", std::vector{n.begin()});
+        throw ParseError("unexpected error: undefined data type for unary operator", std::vector{n.begin()});
       }
       }
     };
@@ -30,9 +31,9 @@ ASTNodeIncDecExpressionBuilder::ASTNodeIncDecExpressionBuilder(ASTNode& n)
     if (not n.children[0]->is_type<language::name>()) {
       if (n.children[0]->is_type<language::post_minusminus>() or n.children[0]->is_type<language::post_plusplus>() or
           n.children[0]->is_type<language::unary_minusminus>() or n.children[0]->is_type<language::unary_plusplus>()) {
-        throw parse_error("chaining ++ or -- operators is not allowed", std::vector{n.children[0]->begin()});
+        throw ParseError("chaining ++ or -- operators is not allowed", std::vector{n.children[0]->begin()});
       } else {
-        throw parse_error("invalid operand type for unary operator", std::vector{n.children[0]->begin()});
+        throw ParseError("invalid operand type for unary operator", std::vector{n.children[0]->begin()});
       }
     }
 
@@ -48,6 +49,6 @@ ASTNodeIncDecExpressionBuilder::ASTNodeIncDecExpressionBuilder(ASTNode& n)
   } else if (n.is_type<language::post_plusplus>()) {
     set_inc_dec_operator_processor(n, language::post_plusplus{});
   } else {
-    throw parse_error("unexpected error: undefined increment/decrement operator", std::vector{n.begin()});
+    throw ParseError("unexpected error: undefined increment/decrement operator", std::vector{n.begin()});
   }
 }
diff --git a/src/language/ast/ASTNodeJumpPlacementChecker.cpp b/src/language/ast/ASTNodeJumpPlacementChecker.cpp
index c699d8dce4866cd229c46c9cd8ab3dde513226f6..3d34b8394ba39031ac04569a158fea2c4d4d5262 100644
--- a/src/language/ast/ASTNodeJumpPlacementChecker.cpp
+++ b/src/language/ast/ASTNodeJumpPlacementChecker.cpp
@@ -1,6 +1,7 @@
 #include <language/ast/ASTNodeJumpPlacementChecker.hpp>
 
 #include <language/PEGGrammar.hpp>
+#include <language/utils/ParseError.hpp>
 
 void
 ASTNodeJumpPlacementChecker::_checkJumpPlacement(ASTNode& n, bool is_inside_loop)
@@ -15,7 +16,7 @@ ASTNodeJumpPlacementChecker::_checkJumpPlacement(ASTNode& n, bool is_inside_loop
       std::ostringstream error_message;
       error_message << "unexpected '" << rang::fgB::red << n.string() << rang::fg::reset
                     << "' outside of loop or switch statement";
-      throw parse_error(error_message.str(), std::vector{n.begin()});
+      throw ParseError(error_message.str(), std::vector{n.begin()});
     }
   } else {
     for (auto& child : n.children) {
diff --git a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
index 48b7e4f5ddacd4b788ff38bf0bceb82442230f70..daa5a0bd371150ffd3d33a60993a3d4d10c48d96 100644
--- a/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeListAffectationExpressionBuilder.cpp
@@ -4,6 +4,7 @@
 #include <language/ast/ASTNodeDataTypeFlattener.hpp>
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 #include <language/node_processor/AffectationProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 template <typename OperatorT>
 void
@@ -33,8 +34,8 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: invalid operand type for affectation",
-                        std::vector{node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: invalid operand type for affectation",
+                       std::vector{node_sub_data_type.m_parent_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -62,17 +63,17 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
           list_affectation_processor->template add<ValueT, ZeroType>(value_node);
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid operand value",
-                            std::vector{node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: invalid operand value",
+                           std::vector{node_sub_data_type.m_parent_node.begin()});
           // LCOV_EXCL_STOP
         }
       } else {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid dimension", std::vector{node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error: invalid dimension", std::vector{node_sub_data_type.m_parent_node.begin()});
         // LCOV_EXCL_STOP
       }
     } else {
-      throw parse_error("unexpected error: invalid value type", std::vector{node_sub_data_type.m_parent_node.begin()});
+      throw ParseError("unexpected error: invalid value type", std::vector{node_sub_data_type.m_parent_node.begin()});
     }
   };
 
@@ -115,8 +116,8 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
         }
           // LCOV_EXCL_START
         default: {
-          throw parse_error("unexpected error: invalid vector dimension",
-                            std::vector{node_sub_data_type.m_parent_node.begin()});
+          throw ParseError("unexpected error: invalid vector dimension",
+                           std::vector{node_sub_data_type.m_parent_node.begin()});
         }
           // LCOV_EXCL_STOP
         }
@@ -124,14 +125,13 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error:invalid operand type for string affectation",
-                          std::vector{node_sub_data_type.m_parent_node.begin()});
+        throw ParseError("unexpected error:invalid operand type for string affectation",
+                         std::vector{node_sub_data_type.m_parent_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
     } else {
-      throw parse_error("unexpected error: undefined operator type for string affectation",
-                        std::vector{m_node.begin()});
+      throw ParseError("unexpected error: undefined operator type for string affectation", std::vector{m_node.begin()});
     }
   };
 
@@ -170,7 +170,7 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("invalid dimension", std::vector{value_node.begin()});
+        throw ParseError("invalid dimension", std::vector{value_node.begin()});
       }
         // LCOV_EXCL_STOP
       }
@@ -182,8 +182,7 @@ ASTNodeListAffectationExpressionBuilder::_buildAffectationProcessor(
     }
       // LCOV_EXCL_START
     default: {
-      throw parse_error("unexpected error: undefined value type for tuple affectation",
-                        std::vector{value_node.begin()});
+      throw ParseError("unexpected error: undefined value type for tuple affectation", std::vector{value_node.begin()});
     }
       // LCOV_EXCL_STOP
     }
@@ -212,7 +211,7 @@ ASTNodeListAffectationExpressionBuilder::_buildListAffectationProcessor()
   ASTNode& name_list_node = *m_node.children[0];
 
   if (name_list_node.children.size() != flattened_rhs_data_type_list.size()) {
-    throw parse_error("incompatible list sizes in affectation", std::vector{m_node.begin()});
+    throw ParseError("incompatible list sizes in affectation", std::vector{m_node.begin()});
   }
 
   using ListAffectationProcessorT = ListAffectationProcessor<OperatorT>;
@@ -234,9 +233,9 @@ ASTNodeListAffectationExpressionBuilder::ASTNodeListAffectationExpressionBuilder
     if (node.is_type<language::eq_op>()) {
       this->_buildListAffectationProcessor<language::eq_op>();
     } else {
-      throw parse_error("undefined affectation operator for tuples", std::vector{node.begin()});
+      throw ParseError("undefined affectation operator for tuples", std::vector{node.begin()});
     }
   } else {
-    throw parse_error("invalid right hand side in tuple affectation", std::vector{node.children[1]->begin()});
+    throw ParseError("invalid right hand side in tuple affectation", std::vector{node.children[1]->begin()});
   }
 }
diff --git a/src/language/ast/ASTNodeNaturalConversionChecker.cpp b/src/language/ast/ASTNodeNaturalConversionChecker.cpp
index b01ff1fc346856797b37f58a20b72039e8b49f59..0faabac4fa7d0cb197ba3b8f35ffcfadce3f162a 100644
--- a/src/language/ast/ASTNodeNaturalConversionChecker.cpp
+++ b/src/language/ast/ASTNodeNaturalConversionChecker.cpp
@@ -1,6 +1,7 @@
 #include <language/ast/ASTNodeNaturalConversionChecker.hpp>
 
 #include <language/PEGGrammar.hpp>
+#include <language/utils/ParseError.hpp>
 #include <utils/Exceptions.hpp>
 
 void
@@ -17,7 +18,7 @@ ASTNodeNaturalConversionChecker::_checkIsNaturalTypeConversion(const ASTNode& no
     if ((data_type == ASTNodeDataType::undefined_t) or (target_data_type == ASTNodeDataType::undefined_t)) {
       throw UnexpectedError(error_message.str());
     } else {
-      throw parse_error(error_message.str(), node.begin());
+      throw ParseError(error_message.str(), node.begin());
     }
   }
 }
@@ -31,7 +32,7 @@ ASTNodeNaturalConversionChecker::_checkIsNaturalExpressionConversion(const ASTNo
     switch (node.m_data_type) {
     case ASTNodeDataType::list_t: {
       if (node.children.size() != target_data_type.dimension()) {
-        throw parse_error("incompatible dimensions in affectation", std::vector{node.begin()});
+        throw ParseError("incompatible dimensions in affectation", std::vector{node.begin()});
       }
       for (const auto& child : node.children) {
         this->_checkIsNaturalExpressionConversion(*child, child->m_data_type, ASTNodeDataType::double_t);
@@ -41,7 +42,7 @@ ASTNodeNaturalConversionChecker::_checkIsNaturalExpressionConversion(const ASTNo
     }
     case ASTNodeDataType::vector_t: {
       if (data_type.dimension() != target_data_type.dimension()) {
-        throw parse_error("incompatible dimensions in affectation", std::vector{node.begin()});
+        throw ParseError("incompatible dimensions in affectation", std::vector{node.begin()});
       }
       break;
     }
diff --git a/src/language/ast/ASTNodeUnaryOperatorExpressionBuilder.cpp b/src/language/ast/ASTNodeUnaryOperatorExpressionBuilder.cpp
index ff081cea9e53378c6ea3639e3b7fd0a7b9162ca3..7d94c3f58fe1762b582a0d984edc797b0fafd021 100644
--- a/src/language/ast/ASTNodeUnaryOperatorExpressionBuilder.cpp
+++ b/src/language/ast/ASTNodeUnaryOperatorExpressionBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/UnaryExpressionProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 ASTNodeUnaryOperatorExpressionBuilder::ASTNodeUnaryOperatorExpressionBuilder(ASTNode& n)
 {
@@ -28,8 +29,8 @@ ASTNodeUnaryOperatorExpressionBuilder::ASTNodeUnaryOperatorExpressionBuilder(AST
         break;
       }
       default: {
-        throw parse_error("unexpected error: invalid operand type for unary operator",
-                          std::vector{n.children[0]->begin()});
+        throw ParseError("unexpected error: invalid operand type for unary operator",
+                         std::vector{n.children[0]->begin()});
       }
       }
     };
@@ -69,19 +70,19 @@ ASTNodeUnaryOperatorExpressionBuilder::ASTNodeUnaryOperatorExpressionBuilder(AST
           }
             // LCOV_EXCL_START
           default: {
-            throw parse_error("unexpected error: invalid vector dimension", std::vector{n.begin()});
+            throw ParseError("unexpected error: invalid vector dimension", std::vector{n.begin()});
           }
             // LCOV_EXCL_STOP
           }
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid unary operator for vector data", std::vector{n.begin()});
+          throw ParseError("unexpected error: invalid unary operator for vector data", std::vector{n.begin()});
           // LCOV_EXCL_STOP
         }
         break;
       }
       default: {
-        throw parse_error("undefined value type for unary operator", std::vector{n.begin()});
+        throw ParseError("undefined value type for unary operator", std::vector{n.begin()});
       }
       }
     };
@@ -94,6 +95,6 @@ ASTNodeUnaryOperatorExpressionBuilder::ASTNodeUnaryOperatorExpressionBuilder(AST
   } else if (n.is_type<language::unary_not>()) {
     set_unary_operator_processor(n, language::unary_not{});
   } else {
-    throw parse_error("unexpected error: undefined unary operator", std::vector{n.begin()});
+    throw ParseError("unexpected error: undefined unary operator", std::vector{n.begin()});
   }
 }
diff --git a/src/language/ast/ASTSymbolInitializationChecker.cpp b/src/language/ast/ASTSymbolInitializationChecker.cpp
index b68e6d05a5872c95c546b108ad6ef8c9256e1965..3a8c846a3626a3acb815a401767d898b378f546c 100644
--- a/src/language/ast/ASTSymbolInitializationChecker.cpp
+++ b/src/language/ast/ASTSymbolInitializationChecker.cpp
@@ -1,6 +1,7 @@
 #include <language/ast/ASTSymbolInitializationChecker.hpp>
 
 #include <language/PEGGrammar.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 
 void
@@ -16,13 +17,13 @@ ASTSymbolInitializationChecker::_checkSymbolInitialization(ASTNode& node)
 
     auto check_correct_name_in_definition = [](ASTNode& decl_name_node, ASTNode& def_name_node) {
       if (def_name_node.is_type<language::name_list>()) {
-        throw parse_error("unexpected variable list, expecting one identifier", std::vector{def_name_node.begin()});
+        throw ParseError("unexpected variable list, expecting one identifier", std::vector{def_name_node.begin()});
       }
       Assert(def_name_node.is_type<language::name>());
       if (decl_name_node.string() != def_name_node.string()) {
         std::ostringstream os;
         os << "invalid identifier, expecting '" << decl_name_node.string() << "'" << std::ends;
-        throw parse_error(os.str(), std::vector{def_name_node.begin()});
+        throw ParseError(os.str(), std::vector{def_name_node.begin()});
       }
     };
 
@@ -44,13 +45,13 @@ ASTSymbolInitializationChecker::_checkSymbolInitialization(ASTNode& node)
           std::ostringstream os;
           os << "invalid number of definition identifiers, expecting " << decl_name_list_node.children.size()
              << " found " << def_name_list_node.children.size() << std::ends;
-          throw parse_error(os.str(), std::vector{def_name_list_node.begin()});
+          throw ParseError(os.str(), std::vector{def_name_list_node.begin()});
         }
         if (def_name_list_node.children.size() != expression_list_node.children.size()) {
           std::ostringstream os;
           os << "invalid number of definition expressions, expecting " << decl_name_list_node.children.size()
              << " found " << expression_list_node.children.size() << std::ends;
-          throw parse_error(os.str(), std::vector{expression_list_node.begin()});
+          throw ParseError(os.str(), std::vector{expression_list_node.begin()});
         }
 
         this->_checkSymbolInitialization(expression_list_node);
@@ -113,7 +114,7 @@ ASTSymbolInitializationChecker::_checkSymbolInitialization(ASTNode& node)
     if (not i_symbol->attributes().isInitialized()) {
       std::ostringstream error_message;
       error_message << "uninitialized symbol '" << rang::fg::red << node.string() << rang::fg::reset << '\'';
-      throw parse_error(error_message.str(), std::vector{node.begin()});
+      throw ParseError(error_message.str(), std::vector{node.begin()});
     }
   }
 
diff --git a/src/language/ast/ASTSymbolTableBuilder.cpp b/src/language/ast/ASTSymbolTableBuilder.cpp
index 2c8f4850a5cd477eac43fb43c30d6eb0d7cca671..feb0b0130dd316df1cf768ad3506fa31f42b68b8 100644
--- a/src/language/ast/ASTSymbolTableBuilder.cpp
+++ b/src/language/ast/ASTSymbolTableBuilder.cpp
@@ -1,6 +1,7 @@
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 
 #include <language/PEGGrammar.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 
 void
@@ -25,7 +26,7 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>
     if (not success) {
       std::ostringstream error_message;
       error_message << "symbol '" << rang::fg::red << symbol << rang::fg::reset << "' was already defined!";
-      throw parse_error(error_message.str(), std::vector{n.begin()});
+      throw ParseError(error_message.str(), std::vector{n.begin()});
     }
 
     for (auto& child : n.children) {
@@ -46,7 +47,7 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>
             std::ostringstream error_message;
             error_message << "symbol '" << rang::fg::red << argument_node.string() << rang::fg::reset
                           << "' was already defined!";
-            throw parse_error(error_message.str(), std::vector{argument_node.begin()});
+            throw ParseError(error_message.str(), std::vector{argument_node.begin()});
           }
         };
 
@@ -65,7 +66,7 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>
             std::ostringstream error_message;
             error_message << "symbol '" << rang::fg::red << argument_node.string() << rang::fg::reset
                           << "' was already defined!";
-            throw parse_error(error_message.str(), std::vector{argument_node.begin()});
+            throw ParseError(error_message.str(), std::vector{argument_node.begin()});
           }
           // Symbols will be initialized at call
           i_symbol->attributes().setIsInitialized();
@@ -84,7 +85,7 @@ ASTSymbolTableBuilder::buildSymbolTable(ASTNode& n, std::shared_ptr<SymbolTable>
         if (not found) {
           std::ostringstream error_message;
           error_message << "undefined symbol '" << rang::fg::red << n.string() << rang::fg::reset << '\'';
-          throw parse_error(error_message.str(), std::vector{n.begin()});
+          throw ParseError(error_message.str(), std::vector{n.begin()});
         }
       }
     }
diff --git a/src/language/modules/ModuleRepository.cpp b/src/language/modules/ModuleRepository.cpp
index 768661e9999a3dd720455f23daa7c252f555d16f..aa5c1fce3ee96c5318d930d75f2047df0d209735 100644
--- a/src/language/modules/ModuleRepository.cpp
+++ b/src/language/modules/ModuleRepository.cpp
@@ -6,6 +6,7 @@
 #include <language/modules/SchemeModule.hpp>
 #include <language/modules/VTKModule.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <utils/PugsAssert.hpp>
 
@@ -41,7 +42,7 @@ ModuleRepository::_populateEmbedderTableT(const ASTNode& module_name_node,
       std::ostringstream error_message;
       error_message << "importing module '" << module_name << "', cannot add symbol '" << symbol_name
                     << "', it is already defined!";
-      throw parse_error(error_message.str(), module_name_node.begin());
+      throw ParseError(error_message.str(), module_name_node.begin());
     }
 
     i_symbol->attributes().setDataType(data_type);
@@ -68,6 +69,6 @@ ModuleRepository::populateSymbolTable(const ASTNode& module_name_node, SymbolTab
     this->_populateEmbedderTableT(module_name_node, populating_module.getNameTypeMap(), ASTNodeDataType::type_name_id_t,
                                   symbol_table, symbol_table.typeEmbedderTable());
   } else {
-    throw parse_error(std::string{"could not find module "} + module_name, std::vector{module_name_node.begin()});
+    throw ParseError(std::string{"could not find module "} + module_name, std::vector{module_name_node.begin()});
   }
 }
diff --git a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
index ec37d74ee5c815695c229d3b93d5d0acdb79bc9d..7ed93683d1b6bcfd54e624385429bce4319976d5 100644
--- a/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
+++ b/src/language/node_processor/ASTNodeExpressionListProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 
 #include <vector>
@@ -61,7 +62,7 @@ class ASTNodeExpressionListProcessor final : public INodeProcessor
           }
             //    LCOV_EXCL_START
           default: {
-            throw parse_error("unexpected function type", m_node.begin());
+            throw ParseError("unexpected function type", m_node.begin());
           }
             //    LCOV_EXCL_STOP
           }
diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp
index 31e1f070f5fadc1366ad4470b49a8052977bfce2..6898bb2c2b25e4fe44a8f6e9ad271930086f0a81 100644
--- a/src/language/node_processor/AffectationProcessor.hpp
+++ b/src/language/node_processor/AffectationProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/PEGGrammar.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/SymbolTable.hpp>
 #include <utils/Exceptions.hpp>
 #include <utils/PugsTraits.hpp>
@@ -86,7 +87,7 @@ class AffectationExecutor final : public IAffectationExecutor
   {
     // LCOV_EXCL_START
     if constexpr (not m_is_defined) {
-      throw parse_error("unexpected error: invalid operands to affectation expression", std::vector{node.begin()});
+      throw ParseError("unexpected error: invalid operands to affectation expression", std::vector{node.begin()});
     }
     // LCOV_EXCL_STOP
   }
@@ -186,7 +187,7 @@ class ComponentAffectationExecutor final : public IAffectationExecutor
   {
     // LCOV_EXCL_START
     if constexpr (not m_is_defined) {
-      throw parse_error("unexpected error: invalid operands to affectation expression", std::vector{node.begin()});
+      throw ParseError("unexpected error: invalid operands to affectation expression", std::vector{node.begin()});
     }
     // LCOV_EXCL_STOP
   }
@@ -204,7 +205,7 @@ class ComponentAffectationExecutor final : public IAffectationExecutor
               index_value = value;
             } else {
               // LCOV_EXCL_START
-              throw parse_error("unexpected error: invalid index type", std::vector{m_index_expression.begin()});
+              throw ParseError("unexpected error: invalid index type", std::vector{m_index_expression.begin()});
               // LCOV_EXCL_STOP
             }
           },
@@ -286,8 +287,8 @@ class AffectationProcessor final : public INodeProcessor
 
       // LCOV_EXCL_START
       if (array_expression.m_data_type != ASTNodeDataType::vector_t) {
-        throw parse_error("unexpected error: invalid lhs (expecting R^d)",
-                          std::vector{array_subscript_expression.begin()});
+        throw ParseError("unexpected error: invalid lhs (expecting R^d)",
+                         std::vector{array_subscript_expression.begin()});
       }
       // LCOV_EXCL_STOP
 
@@ -326,15 +327,14 @@ class AffectationProcessor final : public INodeProcessor
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid vector dimension",
-                          std::vector{array_subscript_expression.begin()});
+        throw ParseError("unexpected error: invalid vector dimension", std::vector{array_subscript_expression.begin()});
       }
         // LCOV_EXCL_STOP
       }
 
     } else {
       // LCOV_EXCL_START
-      throw parse_error("unexpected error: invalid lhs", std::vector{node.children[0]->begin()});
+      throw ParseError("unexpected error: invalid lhs", std::vector{node.children[0]->begin()});
       // LCOV_EXCL_STOP
     }
   }
@@ -366,7 +366,7 @@ class AffectationToTinyVectorFromListProcessor final : public INodeProcessor
             v[i] = child_value;
           } else {
             // LCOV_EXCL_START
-            throw parse_error("unexpected error: unexpected right hand side type in affectation", m_node.begin());
+            throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
             // LCOV_EXCL_STOP
           }
         },
@@ -421,7 +421,7 @@ class AffectationToTupleProcessor final : public INodeProcessor
           *m_lhs = std::vector{TinyVector<1>{static_cast<double>(v)}};
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: unexpected right hand side type in affectation", m_node.begin());
+          throw ParseError("unexpected error: unexpected right hand side type in affectation", m_node.begin());
           // LCOV_EXCL_STOP
         }
       },
@@ -480,8 +480,8 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor
                       v[j] = vj;
                     } else {
                       // LCOV_EXCL_START
-                      throw parse_error("unexpected error: unexpected right hand side type in affectation",
-                                        m_node.children[1]->children[i]->begin());
+                      throw ParseError("unexpected error: unexpected right hand side type in affectation",
+                                       m_node.children[1]->children[i]->begin());
                       // LCOV_EXCL_STOP
                     }
                   },
@@ -493,14 +493,14 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor
               tuple_value[i] = ZeroType{};
             } else {
               // LCOV_EXCL_START
-              throw parse_error("unexpected error: unexpected right hand side type in affectation",
-                                m_node.children[1]->children[i]->begin());
+              throw ParseError("unexpected error: unexpected right hand side type in affectation",
+                               m_node.children[1]->children[i]->begin());
               // LCOV_EXCL_STOP
             }
           } else {
             // LCOV_EXCL_START
-            throw parse_error("unexpected error: unexpected right hand side type in affectation",
-                              m_node.children[1]->children[i]->begin());
+            throw ParseError("unexpected error: unexpected right hand side type in affectation",
+                             m_node.children[1]->children[i]->begin());
             // LCOV_EXCL_STOP
           }
         },
@@ -536,8 +536,8 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor
       }
     } else {
       // LCOV_EXCL_START
-      throw parse_error("unexpected error: unexpected right hand side type in tuple affectation",
-                        m_node.children[1]->begin());
+      throw ParseError("unexpected error: unexpected right hand side type in tuple affectation",
+                       m_node.children[1]->begin());
       // LCOV_EXCL_STOP
     }
 
@@ -559,8 +559,8 @@ class AffectationToTupleFromListProcessor final : public INodeProcessor
           this->_copyVector(value_list);
         } else {
           // LCOV_EXCL_START
-          throw parse_error("unexpected error: invalid lhs (expecting list or tuple)",
-                            std::vector{m_node.children[1]->begin()});
+          throw ParseError("unexpected error: invalid lhs (expecting list or tuple)",
+                           std::vector{m_node.children[1]->begin()});
           // LCOV_EXCL_STOP
         }
       },
@@ -645,8 +645,8 @@ class ListAffectationProcessor final : public INodeProcessor
 
       if (array_expression.m_data_type != ASTNodeDataType::vector_t) {
         // LCOV_EXCL_START
-        throw parse_error("unexpected error: invalid lhs (expecting R^d)",
-                          std::vector{array_subscript_expression.begin()});
+        throw ParseError("unexpected error: invalid lhs (expecting R^d)",
+                         std::vector{array_subscript_expression.begin()});
         // LCOV_EXCL_STOP
       }
 
@@ -685,14 +685,13 @@ class ListAffectationProcessor final : public INodeProcessor
       }
         // LCOV_EXCL_START
       default: {
-        throw parse_error("unexpected error: invalid vector dimension",
-                          std::vector{array_subscript_expression.begin()});
+        throw ParseError("unexpected error: invalid vector dimension", std::vector{array_subscript_expression.begin()});
       }
         // LCOV_EXCL_STOP
       }
     } else {
       // LCOV_EXCL_START
-      throw parse_error("unexpected error: invalid left hand side", std::vector{lhs_node.begin()});
+      throw ParseError("unexpected error: invalid left hand side", std::vector{lhs_node.begin()});
       // LCOV_EXCL_STOP
     }
   }
diff --git a/src/language/node_processor/ArraySubscriptProcessor.hpp b/src/language/node_processor/ArraySubscriptProcessor.hpp
index 4853c65d326657a63599d62fc9d52713a96d1d5f..3a71c01846b9e17ce28818d732716aea764d2496 100644
--- a/src/language/node_processor/ArraySubscriptProcessor.hpp
+++ b/src/language/node_processor/ArraySubscriptProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 template <typename ArrayTypeT>
 class ArraySubscriptProcessor : public INodeProcessor
@@ -25,7 +26,7 @@ class ArraySubscriptProcessor : public INodeProcessor
             index_value = value;
           } else {
             // LCOV_EXCL_START
-            throw parse_error("unexpected error: invalid index type", std::vector{index_expression.begin()});
+            throw ParseError("unexpected error: invalid index type", std::vector{index_expression.begin()});
             // LCOV_EXCL_STOP
           }
         },
diff --git a/src/language/node_processor/BinaryExpressionProcessor.hpp b/src/language/node_processor/BinaryExpressionProcessor.hpp
index 2e4fcef12ffe74dcd5cf972b4353ba88ab8d0e55..8f5d9c3220cf3018104d5d53e2036b9bb8217214 100644
--- a/src/language/node_processor/BinaryExpressionProcessor.hpp
+++ b/src/language/node_processor/BinaryExpressionProcessor.hpp
@@ -4,6 +4,7 @@
 #include <language/PEGGrammar.hpp>
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 template <typename Op>
 struct BinOp;
@@ -211,7 +212,7 @@ class BinaryExpressionProcessor final : public INodeProcessor
   {
     if constexpr (not m_is_defined) {
       // LCOV_EXCL_START
-      throw parse_error("invalid operands to binary expression", std::vector{m_node.begin()});
+      throw ParseError("invalid operands to binary expression", std::vector{m_node.begin()});
       // LCOV_EXCL_STOP
     }
   }
diff --git a/src/language/node_processor/BuiltinFunctionProcessor.hpp b/src/language/node_processor/BuiltinFunctionProcessor.hpp
index 755680f5fee0a2da69e02f0fd04a550e7e971846..302cbaf2d2ce3570678fb62a57d4e463e47a2432 100644
--- a/src/language/node_processor/BuiltinFunctionProcessor.hpp
+++ b/src/language/node_processor/BuiltinFunctionProcessor.hpp
@@ -5,6 +5,7 @@
 #include <language/node_processor/FunctionArgumentConverter.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
 #include <language/utils/BuiltinFunctionEmbedder.hpp>
+#include <language/utils/ParseError.hpp>
 
 #include <utils/SignalManager.hpp>
 
@@ -69,7 +70,7 @@ class BuiltinFunctionProcessor : public INodeProcessor
         return m_function_expression_processor->execute(context_exec_policy);
       }
       catch (std::runtime_error& e) {
-        throw parse_error(e.what(), {m_argument_node.begin()});
+        throw ParseError(e.what(), {m_argument_node.begin()});
       }
     }
   }
diff --git a/src/language/node_processor/OStreamProcessor.hpp b/src/language/node_processor/OStreamProcessor.hpp
index 606247653b397998fab2016b970d87637201c527..e67dd7b6d9fcfa2440a59367efecd42ffd35a8ca 100644
--- a/src/language/node_processor/OStreamProcessor.hpp
+++ b/src/language/node_processor/OStreamProcessor.hpp
@@ -3,6 +3,7 @@
 
 #include <language/ast/ASTNode.hpp>
 #include <language/node_processor/INodeProcessor.hpp>
+#include <language/utils/ParseError.hpp>
 
 class OStreamProcessor final : public INodeProcessor
 {
@@ -27,8 +28,8 @@ class OStreamProcessor final : public INodeProcessor
       if ((child->m_data_type == ASTNodeDataType::type_name_id_t) or
           (child->m_data_type == ASTNodeDataType::function_t) or
           (child->m_data_type == ASTNodeDataType::builtin_function_t)) {
-        throw parse_error("invalid argument, cannot print a '" + dataTypeName(child->m_data_type) + "'",
-                          std::vector{child->begin()});
+        throw ParseError("invalid argument, cannot print a '" + dataTypeName(child->m_data_type) + "'",
+                         std::vector{child->begin()});
       }
     }
   }
diff --git a/src/language/utils/ParseError.hpp b/src/language/utils/ParseError.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c15b07cd49f2c5cd3f8a85d339e006822d2b4c1
--- /dev/null
+++ b/src/language/utils/ParseError.hpp
@@ -0,0 +1,38 @@
+#ifndef PARSE_ERROR_HPP
+#define PARSE_ERROR_HPP
+
+#include <utils/Exceptions.hpp>
+
+#include <pegtl/position.hpp>
+
+#include <vector>
+
+class ParseError : public IExitError
+{
+ private:
+  std::string m_error_msg;
+  std::vector<TAO_PEGTL_NAMESPACE::position> m_positions;
+
+ public:
+  const auto&
+  positions() const
+  {
+    return m_positions;
+  }
+
+  ParseError(const std::string_view msg, std::vector<TAO_PEGTL_NAMESPACE::position>&& positions)
+    : IExitError(msg), m_positions{std::move(positions)}
+  {}
+
+  ParseError(const std::string_view msg, const std::vector<TAO_PEGTL_NAMESPACE::position>& positions)
+    : IExitError(msg), m_positions{positions}
+  {}
+
+  ParseError(const std::string_view msg, const TAO_PEGTL_NAMESPACE::position& positions)
+    : IExitError(msg), m_positions{positions}
+  {}
+
+  ~ParseError() = default;
+};
+
+#endif   // PARSE_ERROR_HPP
diff --git a/tests/test_ASTModulesImporter.cpp b/tests/test_ASTModulesImporter.cpp
index 7ac28918aec2110eefe734b67bf73da2a55ea952..42638d918a362516b53a0f05772096b477f943b4 100644
--- a/tests/test_ASTModulesImporter.cpp
+++ b/tests/test_ASTModulesImporter.cpp
@@ -84,7 +84,7 @@ import unknown_module;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
-      REQUIRE_THROWS_AS(ASTModulesImporter{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTModulesImporter{*ast}, ParseError);
     }
 
     SECTION("symbol already defined")
@@ -98,7 +98,7 @@ import math;
 
       ast->m_symbol_table->add("sin", ast->begin());
 
-      REQUIRE_THROWS_AS(ASTModulesImporter{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTModulesImporter{*ast}, ParseError);
     }
   }
 }
diff --git a/tests/test_ASTNodeDataTypeBuilder.cpp b/tests/test_ASTNodeDataTypeBuilder.cpp
index 9e44625e1ef31b116954e7b6859b1409873b6e11..c5c691a28db39749f1910521b875eca0af0e8271 100644
--- a/tests/test_ASTNodeDataTypeBuilder.cpp
+++ b/tests/test_ASTNodeDataTypeBuilder.cpp
@@ -5,6 +5,7 @@
 #include <language/ast/ASTSymbolTableBuilder.hpp>
 #include <language/utils/ASTNodeDataTypeTraits.hpp>
 #include <language/utils/ASTPrinter.hpp>
+#include <language/utils/ParseError.hpp>
 #include <language/utils/TypeDescriptor.hpp>
 #include <utils/Exceptions.hpp>
 
@@ -825,7 +826,7 @@ let f : R*Z -> B, x -> 3;
         auto ast = ASTBuilder::build(input);
         ASTSymbolTableBuilder{*ast};
 
-        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
       }
 
       SECTION("wrong parameter number 2")
@@ -837,7 +838,7 @@ let f : R -> B, (x,y) -> 3;
         auto ast = ASTBuilder::build(input);
         ASTSymbolTableBuilder{*ast};
 
-        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
       }
 
       SECTION("wrong image size")
@@ -1090,7 +1091,7 @@ not_a_function(2,3);
         auto ast = ASTBuilder::build(input);
         ASTSymbolTableBuilder{*ast};
 
-        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+        REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
       }
     }
   }
@@ -1545,7 +1546,7 @@ if ("string");
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
       ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
     }
   }
 
@@ -1631,7 +1632,7 @@ while ("string");
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
       ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
     }
   }
 
@@ -1717,7 +1718,7 @@ do 1; while ("string");
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
       ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
     }
   }
 
@@ -2036,7 +2037,7 @@ true xor false;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
       ASTSymbolTableBuilder{*ast};
-      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeDataTypeBuilder{*ast}, ParseError);
     }
   }
 }
diff --git a/tests/test_ASTNodeDataTypeChecker.cpp b/tests/test_ASTNodeDataTypeChecker.cpp
index 7ce833b6d43cecaaadf28086435378c0a6788933..e016e1087281b3175eba0076cb21aaab7234c89b 100644
--- a/tests/test_ASTNodeDataTypeChecker.cpp
+++ b/tests/test_ASTNodeDataTypeChecker.cpp
@@ -4,6 +4,7 @@
 #include <language/ast/ASTNodeDataTypeBuilder.hpp>
 #include <language/ast/ASTNodeDataTypeChecker.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/ParseError.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -46,6 +47,6 @@ for(let i:Z, i=0; i<10; ++i) {
 
     ast->children[0]->m_data_type = ASTNodeDataType::undefined_t;
 
-    REQUIRE_THROWS_AS(ASTNodeDataTypeChecker{*ast}, parse_error);
+    REQUIRE_THROWS_AS(ASTNodeDataTypeChecker{*ast}, ParseError);
   }
 }
diff --git a/tests/test_ASTNodeExpressionBuilder.cpp b/tests/test_ASTNodeExpressionBuilder.cpp
index 801d27d01f0dd89f206d5e9902c9e9414687559f..956b5a2527e8e80bb1cef2287f9b2c72a16768aa 100644
--- a/tests/test_ASTNodeExpressionBuilder.cpp
+++ b/tests/test_ASTNodeExpressionBuilder.cpp
@@ -889,6 +889,6 @@ continue;
     // One is sure that language::ignored is not treated so its a good candidate
     // for this test
     ast->children[0]->set_type<language::ignored>();
-    REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, parse_error);
+    REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, ParseError);
   }
 }
diff --git a/tests/test_ASTNodeFunctionExpressionBuilder.cpp b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
index 7e892c86367a3984cab7cb93a4d27631cda52d28..3ce9d041604f5a10cbe9ea97f063c3d24a1fd04d 100644
--- a/tests/test_ASTNodeFunctionExpressionBuilder.cpp
+++ b/tests/test_ASTNodeFunctionExpressionBuilder.cpp
@@ -53,7 +53,7 @@
                                                                                    \
     ASTNodeTypeCleaner<language::var_declaration>{*ast};                           \
     ASTNodeTypeCleaner<language::fct_declaration>{*ast};                           \
-    REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, parse_error);                \
+    REQUIRE_THROWS_AS(ASTNodeExpressionBuilder{*ast}, ParseError);                \
   }
 
 #define CHECK_AST_THROWS_WITH(data, error)                                         \
diff --git a/tests/test_ASTNodeJumpPlacementChecker.cpp b/tests/test_ASTNodeJumpPlacementChecker.cpp
index 419519cd5803473aadb56abea772d537aaf38c2a..631b172775f381405bbc8ff830d19aad92b9c8e5 100644
--- a/tests/test_ASTNodeJumpPlacementChecker.cpp
+++ b/tests/test_ASTNodeJumpPlacementChecker.cpp
@@ -4,6 +4,7 @@
 #include <language/ast/ASTNodeDataTypeBuilder.hpp>
 #include <language/ast/ASTNodeJumpPlacementChecker.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/ParseError.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -76,7 +77,7 @@ do {
 
       ast->children[0]->m_data_type = ASTNodeDataType::undefined_t;
 
-      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, ParseError);
     }
   }
 
@@ -145,7 +146,7 @@ do {
 
       ast->children[0]->m_data_type = ASTNodeDataType::undefined_t;
 
-      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTNodeJumpPlacementChecker{*ast}, ParseError);
     }
   }
 }
diff --git a/tests/test_ASTSymbolTableBuilder.cpp b/tests/test_ASTSymbolTableBuilder.cpp
index 0398914c084e4c264bcae9f28f8356ed5885d3dc..028f79882eed45e8dc99c17efd414ce63c7ddd4e 100644
--- a/tests/test_ASTSymbolTableBuilder.cpp
+++ b/tests/test_ASTSymbolTableBuilder.cpp
@@ -2,6 +2,7 @@
 
 #include <language/ast/ASTBuilder.hpp>
 #include <language/ast/ASTSymbolTableBuilder.hpp>
+#include <language/utils/ParseError.hpp>
 
 #include <pegtl/string_input.hpp>
 
@@ -74,7 +75,7 @@ let n:N, n = a;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
-      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, ParseError);
     }
 
     SECTION("Re-declared symbol")
@@ -87,7 +88,7 @@ let n:N, n = 1;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
-      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, ParseError);
     }
 
     SECTION("Re-declared symbol (function)")
@@ -100,7 +101,7 @@ let f : R -> R, x -> 1;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
-      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, ParseError);
     }
 
     SECTION("Re-declared parameter (function)")
@@ -112,7 +113,7 @@ let f : R*R*N -> R, (x,y,x) -> 1;
       string_input input{data, "test.pgs"};
       auto ast = ASTBuilder::build(input);
 
-      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, parse_error);
+      REQUIRE_THROWS_AS(ASTSymbolTableBuilder{*ast}, ParseError);
     }
   }
 }
diff --git a/tests/test_BuiltinFunctionRegister.hpp b/tests/test_BuiltinFunctionRegister.hpp
index fdf46213388b23e46c1457a56ea213cf17ac83f0..1583f1df99d7eddefc3337529611a5d5720912a0 100644
--- a/tests/test_BuiltinFunctionRegister.hpp
+++ b/tests/test_BuiltinFunctionRegister.hpp
@@ -162,7 +162,7 @@ class test_BuiltinFunctionRegister
       if (not success) {
         std::ostringstream error_message;
         error_message << "cannot add symbol '" << symbol_name << "' it is already defined";
-        throw parse_error(error_message.str(), root_node.begin());
+        throw ParseError(error_message.str(), root_node.begin());
       }
 
       i_symbol->attributes().setDataType(ASTNodeDataType::builtin_function_t);