Repository: whitemech/pylogics (language: python)

Issue:
fix: remove LDL boolean classes
## Proposed changes

Remove LDL boolean classes

Since we delegate string conversion to utility functions, the presence of 'LDLTrue' and 'LDLFalse' is useless.


## Fixes

n/a

## Types of changes

What types of changes does your code introduce?
_Put an `x` in the boxes that apply_

- [X] Bugfix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)

## Checklist

_Put an `x` in the boxes that apply._

- [ ] I have read the [CONTRIBUTING](../blob/master/CONTRIBUTING.md) doc
- [ ] I am making a pull request against the `develop` branch (left side). Also you should start your branch off our `develop`.
- [ ] Lint and unit tests pass locally with my changes
- [ ] I have added tests that prove my fix is effective or that my feature works

## Further comments

n/a


Discussion / hints:

Fix patch:
diff --git a/HISTORY.md b/HISTORY.md
index fa2ecec..7ea17f0 100644
--- a/HISTORY.md
+++ b/HISTORY.md
@@ -1,12 +1,27 @@
 # History
 
-## 0.1.0a0
+## 0.1.0 (2021-06-07)
 
-- Support for Propositional Logic parsing, 
+- Improved to the behaviour of `Not`:
+  - Make `Not` to simplify when argument is a boolean formula. If `Not` is applied to `TrueFormula`, then the output is `FalseFormula`; 
+    likewise, if it is applied to `FalseFormula`, the output is `TrueFormula`.
+  - Fix: replace `__neg__` with `__invert__`
+- Improved simplification of monotone operators: check also
+  the presence of `phi OP ~phi` and reduce according to the 
+  binary operator involved.
+- Added tests to check consistency between code and documentation.
+- Updated grammars so to be compliant with 
+  version `0.2.0` of [this standard](https://marcofavorito.me/tl-grammars/v/7d9a17267fbf525d9a6a1beb92a46f05cf652db6/).
+
+
+
+## 0.1.0a0 (2021-04-23)
+
+- Added support for Propositional Logic parsing, 
   syntax representation and parsing.
-- Support for Linear Temporal Logic
+- Added support for Linear Temporal Logic
   parsing and syntax representation.
-- Support for Past Linear Temporal Logic
+- Added support for Past Linear Temporal Logic
   parsing and syntax representation.
-- Support for Linear Dynamic Logic 
+- Added support for Linear Dynamic Logic 
   parsing and syntax representation.
diff --git a/README.md b/README.md
index 42f7cb5..f41346b 100644
--- a/README.md
+++ b/README.md
@@ -58,7 +58,7 @@ A Python library for logic formalisms representation and manipulation.
 
 To install the package from PyPI:
 ```
-pip install pylogics==0.1.0a0
+pip install pylogics
 ```
 
 ## Tests
diff --git a/docs/grammars.md b/docs/grammars.md
index c954aa1..eef1496 100644
--- a/docs/grammars.md
+++ b/docs/grammars.md
@@ -18,24 +18,24 @@ start: propositional_formula
 ?prop_and: prop_not (AND prop_not)*
 ?prop_not: NOT* prop_wrapped
 ?prop_wrapped: prop_atom
-            | LSEPARATOR propositional_formula RSEPARATOR
+            | LEFT_PARENTHESIS propositional_formula RIGHT_PARENTHESIS
 ?prop_atom: atom
-	     | prop_true
-	     | prop_false
+         | prop_true
+         | prop_false
 
 atom: SYMBOL_NAME
 prop_true: TRUE
 prop_false: FALSE
 
-LSEPARATOR : "("
-RSEPARATOR : ")"
+LEFT_PARENTHESIS : "("
+RIGHT_PARENTHESIS : ")"
 EQUIVALENCE : "<->"
 IMPLY : ">>"|"->"
 OR: "||"|"|"
 AND: "&&"|"&"
 NOT: "!"|"~"
-TRUE.2: /(?i:true)/
-FALSE.2: /(?i:false)/
+TRUE.2: /true/
+FALSE.2: /false/
 
 // Symbols cannot contain uppercase letters, because these are reserved
 // Moreover, any word between quotes is a symbol
@@ -76,28 +76,35 @@ start: ltlf_formula
 ?ltlf_weak_next:   WEAK_NEXT ltlf_unaryop
 ?ltlf_not:         NOT ltlf_unaryop
 ?ltlf_wrapped:     ltlf_atom
-             |     LSEPARATOR ltlf_formula RSEPARATOR
+             |     LEFT_PARENTHESIS ltlf_formula RIGHT_PARENTHESIS
 ?ltlf_atom:        ltlf_symbol
           |        ltlf_true
           |        ltlf_false
+          |        ltlf_tt
+          |        ltlf_ff
           |        ltlf_last
 
 ltlf_symbol: SYMBOL_NAME
 ltlf_true: prop_true
 ltlf_false: prop_false
+ltlf_tt: TT
+ltlf_ff: FF
 ltlf_last: LAST
 
 UNTIL.2: "U"
-RELEASE.2: "R"
+RELEASE.2:  /R|V/
 ALWAYS.2: "G"
 EVENTUALLY.2: "F"
-NEXT.2: "X"
-WEAK_NEXT.2: "N"
+NEXT.2: "X[!]"
+WEAK_NEXT.2: "X"
 WEAK_UNTIL.2: "W"
 STRONG_RELEASE.2: "M"
 
-END.2: /(?i:end)/
-LAST.2: /(?i:last)/
+END.2: /end/
+LAST.2: /last/
+
+TT.2: /tt/
+FF.2: /ff/
 
 %ignore /\s+/
 
@@ -109,8 +116,8 @@ LAST.2: /(?i:last)/
 %import  .pl.AND -> AND
 %import  .pl.EQUIVALENCE -> EQUIVALENCE
 %import  .pl.IMPLY -> IMPLY
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
 ```
 
 ## Past Linear Temporal Logic
@@ -140,23 +147,30 @@ start: pltlf_formula
 ?pltlf_before:       BEFORE pltlf_unaryop
 ?pltlf_not:          NOT pltlf_unaryop
 ?pltlf_wrapped:      pltlf_atom
-             |       LSEPARATOR pltlf_formula RSEPARATOR
+             |       LEFT_PARENTHESIS pltlf_formula RIGHT_PARENTHESIS
 ?pltlf_atom:       pltlf_symbol
            |       pltlf_true
            |       pltlf_false
+           |       pltlf_tt
+           |       pltlf_ff
            |       pltlf_start
+           |       pltlf_first
 
 pltlf_symbol: SYMBOL_NAME
 pltlf_true: prop_true
 pltlf_false: prop_false
+pltlf_tt: TT
+pltlf_ff: FF
 pltlf_start: START
+pltlf_first: FIRST
 
 // Operators must not be part of a word
 SINCE.2: "S"
 HISTORICALLY.2: "H"
 ONCE.2: "O"
 BEFORE.2: "Y"
-START.2: /(?i:start)/
+FIRST.2: /first/
+START.2: /start/
 
 %ignore /\s+/
 
@@ -168,8 +182,10 @@ START.2: /(?i:start)/
 %import  .pl.AND -> AND
 %import  .pl.EQUIVALENCE -> EQUIVALENCE
 %import  .pl.IMPLY -> IMPLY
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
+%import  .ltl.TT -> TT
+%import  .ltl.FF -> FF
 ```
 
 ## Linear Dynamic Logic
@@ -181,32 +197,26 @@ and it is reported below:
 ```lark
 start: ldlf_formula
 
-?ldlf_formula:  ldlf_equivalence
+?ldlf_formula:     ldlf_equivalence
 ?ldlf_equivalence: ldlf_implication (EQUIVALENCE ldlf_implication)*
 ?ldlf_implication: ldlf_or (IMPLY ldlf_or)*
-?ldlf_or: ldlf_and (OR ldlf_and)*
-?ldlf_and: ldlf_unaryop (AND ldlf_unaryop)*
+?ldlf_or:          ldlf_and (OR ldlf_and)*
+?ldlf_and:         ldlf_unaryop (AND ldlf_unaryop)*
 
 ?ldlf_unaryop:     ldlf_box
              |     ldlf_diamond
              |     ldlf_not
              |     ldlf_wrapped
-?ldlf_box: BOXLSEPARATOR regular_expression BOXRSEPARATOR ldlf_unaryop
-?ldlf_diamond: DIAMONDLSEPARATOR regular_expression DIAMONDRSEPARATOR ldlf_unaryop
-?ldlf_not: NOT ldlf_unaryop
+?ldlf_box:         LEFT_SQUARE_BRACKET regular_expression RIGHT_SQUARE_BRACKET ldlf_unaryop
+?ldlf_diamond:     LEFT_ANGLE_BRACKET regular_expression RIGHT_ANGLE_BRACKET ldlf_unaryop
+?ldlf_not:         NOT ldlf_unaryop
 ?ldlf_wrapped:    ldlf_atom
-             |    LSEPARATOR ldlf_formula RSEPARATOR
-?ldlf_atom:   ldlf_tt
-          |   ldlf_ff
-          |   ldlf_last
-          |   ldlf_end
-          |   ldlf_prop_true
-          |   ldlf_prop_false
-          |   ldlf_prop_atom
-
-ldlf_prop_true: TRUE
-ldlf_prop_false: FALSE
-ldlf_prop_atom: SYMBOL_NAME
+             |    LEFT_PARENTHESIS ldlf_formula RIGHT_PARENTHESIS
+?ldlf_atom:       ldlf_tt
+          |       ldlf_ff
+          |       ldlf_last
+          |       ldlf_end
+
 
 ldlf_tt: TT
 ldlf_ff: FF
@@ -218,23 +228,21 @@ regular_expression: re_union
 ?re_union:      re_sequence (UNION re_sequence)*
 ?re_sequence:   re_star (SEQ re_star)*
 ?re_star:       re_test STAR?
-?re_test:       TEST ldlf_formula
+?re_test:       ldlf_formula TEST
         |       re_wrapped
 ?re_wrapped:    re_propositional
-           |    LSEPARATOR regular_expression RSEPARATOR
+           |    LEFT_PARENTHESIS regular_expression RIGHT_PARENTHESIS
 re_propositional: propositional_formula
 
 
-BOXLSEPARATOR: "["
-BOXRSEPARATOR: "]"
-DIAMONDLSEPARATOR: "<"
-DIAMONDRSEPARATOR: ">"
+LEFT_SQUARE_BRACKET: "["
+RIGHT_SQUARE_BRACKET: "]"
+LEFT_ANGLE_BRACKET: "<"
+RIGHT_ANGLE_BRACKET: ">"
 UNION: "+"
 SEQ: ";"
 TEST: "?"
 STAR: "*"
-TT.2: /(?i:tt)/
-FF.2: /(?i:ff)/
 
 %ignore /\s+/
 
@@ -247,8 +255,10 @@ FF.2: /(?i:ff)/
 %import  .pl.OR -> OR
 %import  .pl.AND -> AND
 %import  .pl.NOT -> NOT
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
-%import .ltl.LAST -> LAST
-%import .ltl.END -> END
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
+%import  .ltl.LAST -> LAST
+%import  .ltl.END -> END
+%import  .ltl.TT -> TT
+%import  .ltl.FF -> FF
 ```
diff --git a/docs/introduction.md b/docs/introduction.md
index 3e46092..63443e5 100644
--- a/docs/introduction.md
+++ b/docs/introduction.md
@@ -9,7 +9,7 @@ on [temporal logics](https://plato.stanford.edu/entries/logic-temporal/).
 > API might be broken frequently or may contain bugs :bug:.
 
 > :warning: Docs are not thorough and might be inaccurate.
-> Apologizes.
+> Apologies.
 
 ## Quickstart
 
diff --git a/docs/parsing.md b/docs/parsing.md
index 4be5ae0..183ca96 100644
--- a/docs/parsing.md
+++ b/docs/parsing.md
@@ -17,6 +17,11 @@ The library uses [Lark](https://lark-parser.readthedocs.io/en/latest/)
 to generate the parser automatically.
 The grammar files are reported at [this page](grammars.md).
 
+The syntax for `LTL`, `PLTL` and `LDL`
+aims to be compliant with 
+[this specification](https://marcofavorito.me/tl-grammars/v/7d9a17267fbf525d9a6a1beb92a46f05cf652db6/).
+
+
 ## Symbols
 
 A symbol is determined by the following regular expression:
@@ -45,8 +50,8 @@ pl_formula: pl_formula <-> pl_formula  // equivalence
           | pl_formula &&  pl_formula  // conjunction 
           | !pl_formula                // negation
           | ( pl_formula )             // brackets 
-          | true                       // boolean constant
-          | false                      // boolean constant
+          | true                       // boolean propositional constant
+          | false                      // boolean propositional constant
           | SYMBOL                     // prop. atom
 ```
 
@@ -81,20 +86,26 @@ ltl_formula: ltl_formula <-> ltl_formula  // equivalence
            | ltl_formula M ltl_formula    // strong release 
            | F ltl_formula                // eventually 
            | G ltl_formula                // always 
-           | X ltl_formula                // next 
-           | N ltl_formula                // weak next 
-           | true                         // boolean constant
-           | false                        // boolean constant
-           | SYMBOL                       // prop. atom
+           | X[!] ltl_formula             // next 
+           | X ltl_formula                // weak next 
+           | true                         // boolean propositional constant
+           | false                        // boolean propositional constant
+           | tt                           // boolean logical constant
+           | ff                           // boolean logical constant
+           | SYMBOL                       // propositional atom
 ```
 
 Some examples:
 ```python
 from pylogics.parsers import parse_ltl
+parse_ltl("tt")
+parse_ltl("ff")
+parse_ltl("true")
+parse_ltl("false")
 parse_ltl("a")
 parse_ltl("b")
 parse_ltl("X(a)")
-parse_ltl("N(b)")
+parse_ltl("X[!](b)")
 parse_ltl("F(a)")
 parse_ltl("G(b)")
 parse_ltl("G(a -> b)")
@@ -118,14 +129,20 @@ pltl_formula: pltl_formula <-> pltl_formula  // equivalence
             | H pltl_formula                 // historically
             | O pltl_formula                 // once 
             | Y pltl_formula                 // before 
-            | true                           // boolean constant
-            | false                          // boolean constant
-            | SYMBOL                         // prop. atom
+            | true                           // boolean propositional constant
+            | false                          // boolean propositional constant
+            | tt                             // boolean logical constant
+            | ff                             // boolean logical constant
+            | SYMBOL                         // propositional atom
 ```
 
 Some examples:
 ```python
 from pylogics.parsers import parse_pltl
+parse_pltl("tt")
+parse_pltl("ff")
+parse_pltl("true")
+parse_pltl("false")
 parse_pltl("a")
 parse_pltl("b")
 parse_pltl("Y(a)")
@@ -172,5 +189,5 @@ parse_ldl("<a + b>tt")
 parse_ldl("<a ; b><c>tt")
 parse_ldl("<(a ; b)*><c>tt")
 parse_ldl("<true><a>tt")  # Next a
-parse_ldl("<(?<a>tt;true)*>(<b>tt)")  # (a Until b) in LDLf
+parse_ldl("<(<a>tt?;true)*>(<b>tt)")  # (a Until b) in LDLf
 ```
diff --git a/docs/syntax.md b/docs/syntax.md
index b1e6053..3881ce6 100644
--- a/docs/syntax.md
+++ b/docs/syntax.md
@@ -91,8 +91,8 @@ described above.
 For LDL, you can use the following classes,
 defined in `pylogics.syntax.ldl`:
 
-- `LDLTrue`, the boolean positive constant `tt`
-- `LDLFalse`, the boolean negative constant `ff`
+- `TrueFormula(logic=Logic.LDL)`, the boolean positive constant `tt`
+- `FalseFormula(logic=Logic.LDL)`, the boolean negative constant `ff`
 - `Diamond(regex, ldlf_formula)`
 - `Box(regex, ldlf_formula)`
 
diff --git a/mkdocs.yml b/mkdocs.yml
index 0ce3c53..6663646 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -1,6 +1,7 @@
 site_name: PyLogics Docs
+site_url: "https://whitemech.github.io/pylogics/"
 repo_name: 'marcofavorito/pylogics'
-repo_url: https://github.com/marcofavorito/pylogics
+repo_url: "https://github.com/marcofavorito/pylogics"
 
 nav:
 - Home: index.md
diff --git a/poetry.lock b/poetry.lock
index 068bfe1..584a56b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -37,8 +37,8 @@ category = "dev"
 description = "An abstract syntax tree for Python with inference support."
 name = "astroid"
 optional = false
-python-versions = ">=3.6"
-version = "2.5.3"
+python-versions = "~=3.6"
+version = "2.5.6"
 
 [package.dependencies]
 lazy-object-proxy = ">=1.4.0"
@@ -70,14 +70,14 @@ category = "dev"
 description = "Classes Without Boilerplate"
 name = "attrs"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "20.3.0"
+python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
+version = "21.2.0"
 
 [package.extras]
-dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface", "furo", "sphinx", "pre-commit"]
-docs = ["furo", "sphinx", "zope.interface"]
-tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "zope.interface"]
-tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six"]
+dev = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit"]
+docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
+tests = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface"]
+tests_no_zope = ["coverage (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins"]
 
 [[package]]
 category = "dev"
@@ -143,7 +143,7 @@ description = "Python package for providing Mozilla's CA Bundle."
 name = "certifi"
 optional = false
 python-versions = "*"
-version = "2020.12.5"
+version = "2021.5.30"
 
 [[package]]
 category = "dev"
@@ -169,8 +169,15 @@ category = "dev"
 description = "Composable command line interface toolkit"
 name = "click"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "7.1.2"
+python-versions = ">=3.6"
+version = "8.0.1"
+
+[package.dependencies]
+colorama = "*"
+
+[package.dependencies.importlib-metadata]
+python = "<3.8"
+version = "*"
 
 [[package]]
 category = "dev"
@@ -229,7 +236,7 @@ description = "Decorators for Humans"
 name = "decorator"
 optional = false
 python-versions = ">=3.5"
-version = "5.0.7"
+version = "5.0.9"
 
 [[package]]
 category = "dev"
@@ -245,7 +252,7 @@ description = "Distribution utilities"
 name = "distlib"
 optional = false
 python-versions = "*"
-version = "0.3.1"
+version = "0.3.2"
 
 [[package]]
 category = "dev"
@@ -301,7 +308,7 @@ description = "the modular source code checker: pep8 pyflakes and co"
 name = "flake8"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-version = "3.9.1"
+version = "3.9.2"
 
 [package.dependencies]
 mccabe = ">=0.6.0,<0.7.0"
@@ -370,11 +377,17 @@ test = ["pytest (>=4.0.2,<6)", "toml"]
 
 [[package]]
 category = "dev"
-description = "Clean single-source support for Python 3 and 2"
-name = "future"
+description = "Copy your docs directly to the gh-pages branch."
+name = "ghp-import"
 optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-version = "0.18.2"
+python-versions = "*"
+version = "2.0.1"
+
+[package.dependencies]
+python-dateutil = ">=2.8.1"
+
+[package.extras]
+dev = ["twine", "markdown", "flake8"]
 
 [[package]]
 category = "dev"
@@ -392,26 +405,30 @@ category = "dev"
 description = "Python Git Library"
 name = "gitpython"
 optional = false
-python-versions = ">=3.4"
-version = "3.1.14"
+python-versions = ">=3.5"
+version = "3.1.17"
 
 [package.dependencies]
 gitdb = ">=4.0.1,<5"
 
+[package.dependencies.typing-extensions]
+python = "<3.8"
+version = ">=3.7.4.0"
+
 [[package]]
 category = "dev"
 description = "A library for property-based testing"
 name = "hypothesis"
 optional = false
 python-versions = ">=3.6"
-version = "6.10.0"
+version = "6.13.14"
 
 [package.dependencies]
 attrs = ">=19.2.0"
 sortedcontainers = ">=2.1.0,<3.0.0"
 
 [package.extras]
-all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
+all = ["black (>=19.10b0)", "click (>=7.0)", "django (>=2.2)", "dpcontracts (>=0.4)", "lark-parser (>=0.6.5)", "libcst (>=0.3.16)", "numpy (>=1.9.0)", "pandas (>=0.25)", "pytest (>=4.6)", "python-dateutil (>=1.4)", "pytz (>=2014.1)", "redis (>=3.0.0)", "rich (>=9.0.0)", "importlib-resources (>=3.3.0)", "importlib-metadata (>=3.6)", "backports.zoneinfo (>=0.2.1)", "tzdata (>=2020.4)"]
 cli = ["click (>=7.0)", "black (>=19.10b0)", "rich (>=9.0.0)"]
 codemods = ["libcst (>=0.3.16)"]
 dateutil = ["python-dateutil (>=1.4)"]
@@ -452,7 +469,7 @@ description = "Read metadata from Python packages"
 name = "importlib-metadata"
 optional = false
 python-versions = ">=3.6"
-version = "3.10.1"
+version = "4.5.0"
 
 [package.dependencies]
 zipp = ">=0.5"
@@ -479,7 +496,7 @@ description = "IPython Kernel for Jupyter"
 name = "ipykernel"
 optional = false
 python-versions = ">=3.5"
-version = "5.5.3"
+version = "5.5.5"
 
 [package.dependencies]
 appnope = "*"
@@ -497,7 +514,7 @@ description = "IPython: Productive Interactive Computing"
 name = "ipython"
 optional = false
 python-versions = ">=3.7"
-version = "7.22.0"
+version = "7.24.1"
 
 [package.dependencies]
 appnope = "*"
@@ -505,6 +522,7 @@ backcall = "*"
 colorama = "*"
 decorator = "*"
 jedi = ">=0.16"
+matplotlib-inline = "*"
 pexpect = ">4.3"
 pickleshare = "*"
 prompt-toolkit = ">=2.0.0,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.1.0"
@@ -513,7 +531,7 @@ setuptools = ">=18.5"
 traitlets = ">=4.2"
 
 [package.extras]
-all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.16)", "pygments", "qtconsole", "requests", "testpath"]
+all = ["Sphinx (>=1.3)", "ipykernel", "ipyparallel", "ipywidgets", "nbconvert", "nbformat", "nose (>=0.10.1)", "notebook", "numpy (>=1.17)", "pygments", "qtconsole", "requests", "testpath"]
 doc = ["Sphinx (>=1.3)"]
 kernel = ["ipykernel"]
 nbconvert = ["nbconvert"]
@@ -521,7 +539,7 @@ nbformat = ["nbformat"]
 notebook = ["notebook", "ipywidgets"]
 parallel = ["ipyparallel"]
 qtconsole = ["qtconsole"]
-test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.16)"]
+test = ["nose (>=0.10.1)", "requests", "testpath", "pygments", "nbformat", "ipykernel", "numpy (>=1.17)"]
 
 [[package]]
 category = "dev"
@@ -601,23 +619,14 @@ category = "dev"
 description = "A very fast and expressive template engine."
 name = "jinja2"
 optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "2.11.3"
+python-versions = ">=3.6"
+version = "3.0.1"
 
 [package.dependencies]
-MarkupSafe = ">=0.23"
+MarkupSafe = ">=2.0"
 
 [package.extras]
-i18n = ["Babel (>=0.8)"]
-
-[[package]]
-category = "dev"
-description = "Lightweight pipelining with Python functions"
-marker = "python_version > \"2.7\""
-name = "joblib"
-optional = false
-python-versions = ">=3.6"
-version = "1.0.1"
+i18n = ["Babel (>=2.7)"]
 
 [[package]]
 category = "dev"
@@ -751,9 +760,10 @@ description = "a modern parsing library"
 name = "lark-parser"
 optional = false
 python-versions = "*"
-version = "0.11.2"
+version = "0.11.3"
 
 [package.extras]
+atomic_cache = ["atomicwrites"]
 nearley = ["js2py"]
 regex = ["regex"]
 
@@ -765,41 +775,6 @@ optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 version = "1.6.0"
 
-[[package]]
-category = "dev"
-description = "Python LiveReload is an awesome tool for web developers"
-name = "livereload"
-optional = false
-python-versions = "*"
-version = "2.6.3"
-
-[package.dependencies]
-six = "*"
-
-[package.dependencies.tornado]
-python = ">=2.8"
-version = "*"
-
-[[package]]
-category = "dev"
-description = "A Python implementation of Lunr.js"
-name = "lunr"
-optional = false
-python-versions = "*"
-version = "0.5.8"
-
-[package.dependencies]
-future = ">=0.16.0"
-six = ">=1.11.0"
-
-[package.dependencies.nltk]
-optional = true
-python = ">=2.8"
-version = ">=3.2.5"
-
-[package.extras]
-languages = ["nltk (>=3.2.5,<3.5)", "nltk (>=3.2.5)"]
-
 [[package]]
 category = "dev"
 description = "Python implementation of Markdown."
@@ -832,8 +807,19 @@ category = "dev"
 description = "Safely add untrusted strings to HTML/XML markup."
 name = "markupsafe"
 optional = false
-python-versions = ">=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*"
-version = "1.1.1"
+python-versions = ">=3.6"
+version = "2.0.1"
+
+[[package]]
+category = "dev"
+description = "Inline Matplotlib backend for Jupyter"
+name = "matplotlib-inline"
+optional = false
+python-versions = ">=3.5"
+version = "0.1.2"
+
+[package.dependencies]
+traitlets = "*"
 
 [[package]]
 category = "dev"
@@ -845,31 +831,42 @@ version = "0.6.1"
 
 [[package]]
 category = "dev"
-description = "The fastest markdown parser in pure Python"
+description = "A deep merge function for 🐍."
+name = "mergedeep"
+optional = false
+python-versions = ">=3.6"
+version = "1.3.4"
+
+[[package]]
+category = "dev"
+description = "A sane Markdown parser with useful plugins and renderers"
 name = "mistune"
 optional = false
 python-versions = "*"
-version = "0.8.4"
+version = "2.0.0rc1"
 
 [[package]]
 category = "dev"
 description = "Project documentation with Markdown."
 name = "mkdocs"
 optional = false
-python-versions = ">=3.5"
-version = "1.1.2"
+python-versions = ">=3.6"
+version = "1.2"
 
 [package.dependencies]
 Jinja2 = ">=2.10.1"
 Markdown = ">=3.2.1"
 PyYAML = ">=3.10"
 click = ">=3.3"
-livereload = ">=2.5.1"
-tornado = ">=5.0"
+ghp-import = ">=1.0"
+importlib-metadata = ">=3.10"
+mergedeep = ">=1.3.4"
+packaging = ">=20.5"
+pyyaml-env-tag = ">=0.1"
+watchdog = ">=2.0"
 
-[package.dependencies.lunr]
-extras = ["languages"]
-version = "0.5.8"
+[package.extras]
+i18n = ["babel (>=2.9.0)"]
 
 [[package]]
 category = "dev"
@@ -877,7 +874,7 @@ description = "A Material Design theme for MkDocs"
 name = "mkdocs-material"
 optional = false
 python-versions = "*"
-version = "7.1.2"
+version = "7.1.7"
 
 [package.dependencies]
 Pygments = ">=2.4"
@@ -903,7 +900,7 @@ description = "Plugin for mkdocs to generate markdown documents from jupyter not
 name = "mknotebooks"
 optional = false
 python-versions = "*"
-version = "0.7.0.post0.dev8"
+version = "0.7.0.post0.dev98"
 
 [package.dependencies]
 gitpython = "*"
@@ -1012,36 +1009,13 @@ optional = false
 python-versions = ">=3.5"
 version = "1.5.1"
 
-[[package]]
-category = "dev"
-description = "Natural Language Toolkit"
-marker = "python_version > \"2.7\""
-name = "nltk"
-optional = false
-python-versions = ">=3.5.*"
-version = "3.6.1"
-
-[package.dependencies]
-click = "*"
-joblib = "*"
-regex = "*"
-tqdm = "*"
-
-[package.extras]
-all = ["requests", "pyparsing", "numpy", "twython", "gensim (<4.0.0)", "scikit-learn", "scipy", "python-crfsuite", "matplotlib"]
-corenlp = ["requests"]
-machine_learning = ["gensim (<4.0.0)", "numpy", "python-crfsuite", "scikit-learn", "scipy"]
-plot = ["matplotlib"]
-tgrep = ["pyparsing"]
-twitter = ["twython"]
-
 [[package]]
 category = "dev"
 description = "A web-based notebook environment for interactive computing"
 name = "notebook"
 optional = false
 python-versions = ">=3.6"
-version = "6.3.0"
+version = "6.4.0"
 
 [package.dependencies]
 Send2Trash = ">=1.5.0"
@@ -1060,7 +1034,7 @@ tornado = ">=6.1"
 traitlets = ">=4.2.1"
 
 [package.extras]
-docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme"]
+docs = ["sphinx", "nbsphinx", "sphinxcontrib-github-alt", "sphinx-rtd-theme", "myst-parser"]
 json-logging = ["json-logging"]
 test = ["pytest", "coverage", "requests", "nbval", "selenium", "pytest-cov", "requests-unixsocket"]
 
@@ -1109,7 +1083,7 @@ description = "Python Build Reasonableness"
 name = "pbr"
 optional = false
 python-versions = ">=2.6"
-version = "5.5.1"
+version = "5.6.0"
 
 [[package]]
 category = "dev"
@@ -1164,7 +1138,7 @@ description = "Python client for the Prometheus monitoring system."
 name = "prometheus-client"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-version = "0.10.1"
+version = "0.11.0"
 
 [package.extras]
 twisted = ["twisted"]
@@ -1219,11 +1193,14 @@ description = "Python docstring style checker"
 name = "pydocstyle"
 optional = false
 python-versions = ">=3.6"
-version = "6.0.0"
+version = "6.1.1"
 
 [package.dependencies]
 snowballstemmer = "*"
 
+[package.extras]
+toml = ["toml"]
+
 [[package]]
 category = "dev"
 description = "passive checker of Python programs"
@@ -1238,7 +1215,7 @@ description = "Pygments is a syntax highlighting package written in Python."
 name = "pygments"
 optional = false
 python-versions = ">=3.5"
-version = "2.8.1"
+version = "2.9.0"
 
 [[package]]
 category = "dev"
@@ -1246,25 +1223,22 @@ description = "python code static checker"
 name = "pylint"
 optional = false
 python-versions = "~=3.6"
-version = "2.7.4"
+version = "2.8.3"
 
 [package.dependencies]
-astroid = ">=2.5.2,<2.7"
+astroid = "2.5.6"
 colorama = "*"
 isort = ">=4.2.5,<6"
 mccabe = ">=0.6,<0.7"
 toml = ">=0.7.1"
 
-[package.extras]
-docs = ["sphinx (3.5.1)", "python-docs-theme (2020.12)"]
-
 [[package]]
 category = "dev"
 description = "Extension pack for Python Markdown."
 name = "pymdown-extensions"
 optional = false
 python-versions = ">=3.6"
-version = "8.1.1"
+version = "8.2"
 
 [package.dependencies]
 Markdown = ">=3.2"
@@ -1291,7 +1265,7 @@ description = "pytest: simple powerful testing with Python"
 name = "pytest"
 optional = false
 python-versions = ">=3.6"
-version = "6.2.3"
+version = "6.2.4"
 
 [package.dependencies]
 atomicwrites = ">=1.0"
@@ -1316,14 +1290,15 @@ description = "Pytest plugin for measuring coverage."
 name = "pytest-cov"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-version = "2.11.1"
+version = "2.12.1"
 
 [package.dependencies]
 coverage = ">=5.2.1"
 pytest = ">=4.6"
+toml = "*"
 
 [package.extras]
-testing = ["fields", "hunter", "process-tests (2.0.2)", "six", "pytest-xdist", "virtualenv"]
+testing = ["fields", "hunter", "process-tests", "six", "pytest-xdist", "virtualenv"]
 
 [[package]]
 category = "dev"
@@ -1331,7 +1306,7 @@ description = "Pytest plugin to randomly order tests and control random.seed."
 name = "pytest-randomly"
 optional = false
 python-versions = ">=3.6"
-version = "3.7.0"
+version = "3.8.0"
 
 [package.dependencies]
 pytest = "*"
@@ -1358,7 +1333,7 @@ marker = "sys_platform == \"win32\""
 name = "pywin32"
 optional = false
 python-versions = "*"
-version = "300"
+version = "301"
 
 [[package]]
 category = "dev"
@@ -1371,12 +1346,12 @@ version = "0.2.0"
 
 [[package]]
 category = "dev"
-description = "Python bindings for the winpty library"
+description = "Pseudo terminal support for Windows from Python."
 marker = "os_name == \"nt\""
 name = "pywinpty"
 optional = false
-python-versions = "*"
-version = "0.5.7"
+python-versions = ">=3.6"
+version = "1.1.1"
 
 [[package]]
 category = "dev"
@@ -1386,13 +1361,24 @@ optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
 version = "5.4.1"
 
+[[package]]
+category = "dev"
+description = "A custom YAML tag for referencing environment variables in YAML files. "
+name = "pyyaml-env-tag"
+optional = false
+python-versions = ">=3.6"
+version = "0.1"
+
+[package.dependencies]
+pyyaml = "*"
+
 [[package]]
 category = "dev"
 description = "Python bindings for 0MQ"
 name = "pyzmq"
 optional = false
 python-versions = ">=3.6"
-version = "22.0.3"
+version = "22.1.0"
 
 [package.dependencies]
 cffi = "*"
@@ -1404,7 +1390,7 @@ description = "Jupyter Qt console"
 name = "qtconsole"
 optional = false
 python-versions = ">= 3.6"
-version = "5.0.3"
+version = "5.1.0"
 
 [package.dependencies]
 ipykernel = ">=4.1"
@@ -1488,7 +1474,7 @@ description = "Validating URI References per RFC 3986"
 name = "rfc3986"
 optional = false
 python-versions = "*"
-version = "1.4.0"
+version = "1.5.0"
 
 [package.extras]
 idna2008 = ["idna"]
@@ -1535,7 +1521,7 @@ description = "Python 2 and 3 compatibility utilities"
 name = "six"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*"
-version = "1.15.0"
+version = "1.16.0"
 
 [[package]]
 category = "dev"
@@ -1559,7 +1545,7 @@ description = "Sorted Containers -- Sorted List, Sorted Dict, Sorted Set"
 name = "sortedcontainers"
 optional = false
 python-versions = "*"
-version = "2.3.0"
+version = "2.4.0"
 
 [[package]]
 category = "dev"
@@ -1582,11 +1568,11 @@ description = "Tornado websocket backend for the Xterm.js Javascript terminal em
 name = "terminado"
 optional = false
 python-versions = ">=3.6"
-version = "0.9.4"
+version = "0.10.0"
 
 [package.dependencies]
 ptyprocess = "*"
-pywinpty = ">=0.5"
+pywinpty = ">=1.1.0"
 tornado = ">=4"
 
 [package.extras]
@@ -1610,11 +1596,11 @@ category = "dev"
 description = "Test utilities for code working with files and commands"
 name = "testpath"
 optional = false
-python-versions = "*"
-version = "0.4.4"
+python-versions = ">= 3.5"
+version = "0.5.0"
 
 [package.extras]
-test = ["pathlib2"]
+test = ["pytest", "pathlib2"]
 
 [[package]]
 category = "dev"
@@ -1638,7 +1624,7 @@ description = "tox is a generic virtualenv management and test command line tool
 name = "tox"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
-version = "3.23.0"
+version = "3.23.1"
 
 [package.dependencies]
 colorama = ">=0.4.1"
@@ -1664,7 +1650,7 @@ description = "Fast, Extensible Progress Meter"
 name = "tqdm"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
-version = "4.60.0"
+version = "4.61.0"
 
 [package.extras]
 dev = ["py-make (>=0.1.0)", "twine", "wheel"]
@@ -1718,7 +1704,7 @@ description = "Backported and Experimental Type Hints for Python 3.5+"
 name = "typing-extensions"
 optional = false
 python-versions = "*"
-version = "3.7.4.3"
+version = "3.10.0.0"
 
 [[package]]
 category = "dev"
@@ -1726,7 +1712,7 @@ description = "HTTP library with thread-safe connection pooling, file post, and
 name = "urllib3"
 optional = false
 python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-version = "1.26.4"
+version = "1.26.5"
 
 [package.extras]
 brotli = ["brotlipy (>=0.6.0)"]
@@ -1739,7 +1725,7 @@ description = "Virtual Python Environment builder"
 name = "virtualenv"
 optional = false
 python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,>=2.7"
-version = "20.4.3"
+version = "20.4.7"
 
 [package.dependencies]
 appdirs = ">=1.4.3,<2"
@@ -1766,6 +1752,17 @@ version = "2.3"
 [package.dependencies]
 toml = "*"
 
+[[package]]
+category = "dev"
+description = "Filesystem events monitoring"
+name = "watchdog"
+optional = false
+python-versions = ">=3.6"
+version = "2.1.2"
+
+[package.extras]
+watchmedo = ["PyYAML (>=3.10)", "argh (>=0.24.1)"]
+
 [[package]]
 category = "dev"
 description = "Measures the displayed width of unicode strings in a terminal"
@@ -1804,7 +1801,6 @@ version = "1.12.1"
 [[package]]
 category = "dev"
 description = "Backport of pathlib-compatible object wrapper for zip files"
-marker = "python_version < \"3.8\""
 name = "zipp"
 optional = false
 python-versions = ">=3.6"
@@ -1815,7 +1811,7 @@ docs = ["sphinx", "jaraco.packaging (>=8.2)", "rst.linker (>=1.9)"]
 testing = ["pytest (>=4.6)", "pytest-checkdocs (>=1.2.3)", "pytest-flake8", "pytest-cov", "pytest-enabler", "jaraco.itertools", "func-timeout", "pytest-black (>=0.3.7)", "pytest-mypy"]
 
 [metadata]
-content-hash = "280cbb9335874b5fd38864a102111672882416381cd6863c1377659d53c2b912"
+content-hash = "b4a341483b22b292f3a3e585f7cca32fb5b6e9925fff7a530bba453c6bb486d4"
 python-versions = "^3.7"
 
 [metadata.files]
@@ -1848,8 +1844,8 @@ argon2-cffi = [
     {file = "argon2_cffi-20.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:8a84934bd818e14a17943de8099d41160da4a336bcc699bb4c394bbb9b94bd32"},
 ]
 astroid = [
-    {file = "astroid-2.5.3-py3-none-any.whl", hash = "sha256:bea3f32799fbb8581f58431c12591bc20ce11cbc90ad82e2ea5717d94f2080d5"},
-    {file = "astroid-2.5.3.tar.gz", hash = "sha256:ad63b8552c70939568966811a088ef0bc880f99a24a00834abd0e3681b514f91"},
+    {file = "astroid-2.5.6-py3-none-any.whl", hash = "sha256:4db03ab5fc3340cf619dbc25e42c2cc3755154ce6009469766d7143d1fc2ee4e"},
+    {file = "astroid-2.5.6.tar.gz", hash = "sha256:8a398dfce302c13f14bab13e2b14fe385d32b73f4e4853b9bdfb64598baa1975"},
 ]
 async-generator = [
     {file = "async_generator-1.10-py3-none-any.whl", hash = "sha256:01c7bf666359b4967d2cda0000cc2e4af16a0ae098cbffcb8472fb9e8ad6585b"},
@@ -1860,8 +1856,8 @@ atomicwrites = [
     {file = "atomicwrites-1.4.0.tar.gz", hash = "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"},
 ]
 attrs = [
-    {file = "attrs-20.3.0-py2.py3-none-any.whl", hash = "sha256:31b2eced602aa8423c2aea9c76a724617ed67cf9513173fd3a4f03e3a929c7e6"},
-    {file = "attrs-20.3.0.tar.gz", hash = "sha256:832aa3cde19744e49938b91fea06d69ecb9e649c93ba974535d08ad92164f700"},
+    {file = "attrs-21.2.0-py2.py3-none-any.whl", hash = "sha256:149e90d6d8ac20db7a955ad60cf0e6881a3f20d37096140088356da6c716b0b1"},
+    {file = "attrs-21.2.0.tar.gz", hash = "sha256:ef6aaac3ca6cd92904cdd0d83f629a15f18053ec84e6432106f7a4d04ae4f5fb"},
 ]
 backcall = [
     {file = "backcall-0.2.0-py2.py3-none-any.whl", hash = "sha256:fbbce6a29f263178a1f7915c1940bde0ec2b2a967566fe1c65c1dfb7422bd255"},
@@ -1879,8 +1875,8 @@ bleach = [
     {file = "bleach-3.3.0.tar.gz", hash = "sha256:98b3170739e5e83dd9dc19633f074727ad848cbedb6026708c8ac2d3b697a433"},
 ]
 certifi = [
-    {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
-    {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
+    {file = "certifi-2021.5.30-py2.py3-none-any.whl", hash = "sha256:50b1e4f8446b06f41be7dd6338db18e0990601dce795c2b1686458aa7e8fa7d8"},
+    {file = "certifi-2021.5.30.tar.gz", hash = "sha256:2bbf76fd432960138b3ef6dda3dde0544f27cbf8546c458e60baf371917ba9ee"},
 ]
 cffi = [
     {file = "cffi-1.14.5-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:bb89f306e5da99f4d922728ddcd6f7fcebb3241fc40edebcb7284d7514741991"},
@@ -1926,8 +1922,8 @@ chardet = [
     {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
 ]
 click = [
-    {file = "click-7.1.2-py2.py3-none-any.whl", hash = "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"},
-    {file = "click-7.1.2.tar.gz", hash = "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a"},
+    {file = "click-8.0.1-py3-none-any.whl", hash = "sha256:fba402a4a47334742d782209a7c79bc448911afe1149d07bdabdf480b3e2f4b6"},
+    {file = "click-8.0.1.tar.gz", hash = "sha256:8c04c11192119b1ef78ea049e0a6f0463e4c48ef00a30160c704337586f3ad7a"},
 ]
 codecov = [
     {file = "codecov-2.1.11-py2.py3-none-any.whl", hash = "sha256:ba8553a82942ce37d4da92b70ffd6d54cf635fc1793ab0a7dc3fecd6ebfb3df8"},
@@ -2007,16 +2003,16 @@ cryptography = [
     {file = "cryptography-3.4.7.tar.gz", hash = "sha256:3d10de8116d25649631977cb37da6cbdd2d6fa0e0281d014a5b7d337255ca713"},
 ]
 decorator = [
-    {file = "decorator-5.0.7-py3-none-any.whl", hash = "sha256:945d84890bb20cc4a2f4a31fc4311c0c473af65ea318617f13a7257c9a58bc98"},
-    {file = "decorator-5.0.7.tar.gz", hash = "sha256:6f201a6c4dac3d187352661f508b9364ec8091217442c9478f1f83c003a0f060"},
+    {file = "decorator-5.0.9-py3-none-any.whl", hash = "sha256:6e5c199c16f7a9f0e3a61a4a54b3d27e7dad0dbdde92b944426cb20914376323"},
+    {file = "decorator-5.0.9.tar.gz", hash = "sha256:72ecfba4320a893c53f9706bebb2d55c270c1e51a28789361aa93e4a21319ed5"},
 ]
 defusedxml = [
     {file = "defusedxml-0.7.1-py2.py3-none-any.whl", hash = "sha256:a352e7e428770286cc899e2542b6cdaedb2b4953ff269a210103ec58f6198a61"},
     {file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
 ]
 distlib = [
-    {file = "distlib-0.3.1-py2.py3-none-any.whl", hash = "sha256:8c09de2c67b3e7deef7184574fc060ab8a793e7adbb183d942c389c8b13c52fb"},
-    {file = "distlib-0.3.1.zip", hash = "sha256:edf6116872c863e1aa9d5bb7cb5e05a022c519a4594dc703843343a9ddd9bff1"},
+    {file = "distlib-0.3.2-py2.py3-none-any.whl", hash = "sha256:23e223426b28491b1ced97dc3bbe183027419dfc7982b4fa2f05d5f3ff10711c"},
+    {file = "distlib-0.3.2.zip", hash = "sha256:106fef6dc37dd8c0e2c0a60d3fca3e77460a48907f335fa28420463a6f799736"},
 ]
 docutils = [
     {file = "docutils-0.17.1-py2.py3-none-any.whl", hash = "sha256:cf316c8370a737a022b72b56874f6602acf974a37a9fba42ec2876387549fc61"},
@@ -2038,8 +2034,8 @@ filelock = [
     {file = "filelock-3.0.12.tar.gz", hash = "sha256:18d82244ee114f543149c66a6e0c14e9c4f8a1044b5cdaadd0f82159d6a6ff59"},
 ]
 flake8 = [
-    {file = "flake8-3.9.1-py2.py3-none-any.whl", hash = "sha256:3b9f848952dddccf635be78098ca75010f073bfe14d2c6bda867154bea728d2a"},
-    {file = "flake8-3.9.1.tar.gz", hash = "sha256:1aa8990be1e689d96c745c5682b687ea49f2e05a443aff1f8251092b0014e378"},
+    {file = "flake8-3.9.2-py2.py3-none-any.whl", hash = "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907"},
+    {file = "flake8-3.9.2.tar.gz", hash = "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b"},
 ]
 flake8-bugbear = [
     {file = "flake8-bugbear-21.4.3.tar.gz", hash = "sha256:2346c81f889955b39e4a368eb7d508de723d9de05716c287dc860a4073dc57e7"},
@@ -2057,20 +2053,20 @@ flake8-isort = [
     {file = "flake8-isort-4.0.0.tar.gz", hash = "sha256:2b91300f4f1926b396c2c90185844eb1a3d5ec39ea6138832d119da0a208f4d9"},
     {file = "flake8_isort-4.0.0-py2.py3-none-any.whl", hash = "sha256:729cd6ef9ba3659512dee337687c05d79c78e1215fdf921ed67e5fe46cce2f3c"},
 ]
-future = [
-    {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"},
+ghp-import = [
+    {file = "ghp-import-2.0.1.tar.gz", hash = "sha256:753de2eace6e0f7d4edfb3cce5e3c3b98cd52aadb80163303d1d036bda7b4483"},
 ]
 gitdb = [
     {file = "gitdb-4.0.7-py3-none-any.whl", hash = "sha256:6c4cc71933456991da20917998acbe6cf4fb41eeaab7d6d67fbc05ecd4c865b0"},
     {file = "gitdb-4.0.7.tar.gz", hash = "sha256:96bf5c08b157a666fec41129e6d327235284cca4c81e92109260f353ba138005"},
 ]
 gitpython = [
-    {file = "GitPython-3.1.14-py3-none-any.whl", hash = "sha256:3283ae2fba31c913d857e12e5ba5f9a7772bbc064ae2bb09efafa71b0dd4939b"},
-    {file = "GitPython-3.1.14.tar.gz", hash = "sha256:be27633e7509e58391f10207cd32b2a6cf5b908f92d9cd30da2e514e1137af61"},
+    {file = "GitPython-3.1.17-py3-none-any.whl", hash = "sha256:29fe82050709760081f588dd50ce83504feddbebdc4da6956d02351552b1c135"},
+    {file = "GitPython-3.1.17.tar.gz", hash = "sha256:ee24bdc93dce357630764db659edaf6b8d664d4ff5447ccfeedd2dc5c253f41e"},
 ]
 hypothesis = [
-    {file = "hypothesis-6.10.0-py3-none-any.whl", hash = "sha256:1a00a4e3ef40041b2c072deab9c71e307bc5354bb4a102ef22ae8c23fa8b3970"},
-    {file = "hypothesis-6.10.0.tar.gz", hash = "sha256:9f8c130b1278d30cb3cc64e3a4aacb88c4906155772beed90ce33e06794e1ee0"},
+    {file = "hypothesis-6.13.14-py3-none-any.whl", hash = "sha256:8f9346a60183d7e9213f6af4d0b3ce19130530884d9444087263775da327d81d"},
+    {file = "hypothesis-6.13.14.tar.gz", hash = "sha256:36ef2d58f600be2973f694f45a55a5502de705d7594f9cf841276aec9082c414"},
 ]
 hypothesis-pytest = [
     {file = "hypothesis-pytest-0.19.0.tar.gz", hash = "sha256:550fd3a651eb13d399efba3172bd010a42f6c34c2baa04cf072bda0911054e06"},
@@ -2080,20 +2076,20 @@ idna = [
     {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
 ]
 importlib-metadata = [
-    {file = "importlib_metadata-3.10.1-py3-none-any.whl", hash = "sha256:2ec0faae539743ae6aaa84b49a169670a465f7f5d64e6add98388cc29fd1f2f6"},
-    {file = "importlib_metadata-3.10.1.tar.gz", hash = "sha256:c9356b657de65c53744046fa8f7358afe0714a1af7d570c00c3835c2d724a7c1"},
+    {file = "importlib_metadata-4.5.0-py3-none-any.whl", hash = "sha256:833b26fb89d5de469b24a390e9df088d4e52e4ba33b01dc5e0e4f41b81a16c00"},
+    {file = "importlib_metadata-4.5.0.tar.gz", hash = "sha256:b142cc1dd1342f31ff04bb7d022492b09920cb64fed867cd3ea6f80fe3ebd139"},
 ]
 iniconfig = [
     {file = "iniconfig-1.1.1-py2.py3-none-any.whl", hash = "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3"},
     {file = "iniconfig-1.1.1.tar.gz", hash = "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32"},
 ]
 ipykernel = [
-    {file = "ipykernel-5.5.3-py3-none-any.whl", hash = "sha256:21abd584543759e49010975a4621603b3cf871b1039cb3879a14094717692614"},
-    {file = "ipykernel-5.5.3.tar.gz", hash = "sha256:a682e4f7affd86d9ce9b699d21bcab6d5ec9fbb2bfcb194f2706973b252bc509"},
+    {file = "ipykernel-5.5.5-py3-none-any.whl", hash = "sha256:29eee66548ee7c2edb7941de60c0ccf0a7a8dd957341db0a49c5e8e6a0fcb712"},
+    {file = "ipykernel-5.5.5.tar.gz", hash = "sha256:e976751336b51082a89fc2099fb7f96ef20f535837c398df6eab1283c2070884"},
 ]
 ipython = [
-    {file = "ipython-7.22.0-py3-none-any.whl", hash = "sha256:c0ce02dfaa5f854809ab7413c601c4543846d9da81010258ecdab299b542d199"},
-    {file = "ipython-7.22.0.tar.gz", hash = "sha256:9c900332d4c5a6de534b4befeeb7de44ad0cc42e8327fa41b7685abde58cec74"},
+    {file = "ipython-7.24.1-py3-none-any.whl", hash = "sha256:d513e93327cf8657d6467c81f1f894adc125334ffe0e4ddd1abbb1c78d828703"},
+    {file = "ipython-7.24.1.tar.gz", hash = "sha256:9bc24a99f5d19721fb8a2d1408908e9c0520a17fff2233ffe82620847f17f1b6"},
 ]
 ipython-genutils = [
     {file = "ipython_genutils-0.2.0-py2.py3-none-any.whl", hash = "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8"},
@@ -2116,12 +2112,8 @@ jeepney = [
     {file = "jeepney-0.6.0.tar.gz", hash = "sha256:7d59b6622675ca9e993a6bd38de845051d315f8b0c72cca3aef733a20b648657"},
 ]
 jinja2 = [
-    {file = "Jinja2-2.11.3-py2.py3-none-any.whl", hash = "sha256:03e47ad063331dd6a3f04a43eddca8a966a26ba0c5b7207a9a9e4e08f1b29419"},
-    {file = "Jinja2-2.11.3.tar.gz", hash = "sha256:a6d58433de0ae800347cab1fa3043cebbabe8baa9d29e668f1c768cb87a333c6"},
-]
-joblib = [
-    {file = "joblib-1.0.1-py3-none-any.whl", hash = "sha256:feeb1ec69c4d45129954f1b7034954241eedfd6ba39b5e9e4b6883be3332d5e5"},
-    {file = "joblib-1.0.1.tar.gz", hash = "sha256:9c17567692206d2f3fb9ecf5e991084254fe631665c450b443761c4186a613f7"},
+    {file = "Jinja2-3.0.1-py3-none-any.whl", hash = "sha256:1f06f2da51e7b56b8f238affdd6b4e2c61e39598a378cc49345bc1bd42a978a4"},
+    {file = "Jinja2-3.0.1.tar.gz", hash = "sha256:703f484b47a6af502e743c9122595cc812b0271f661722403114f71a79d0f5a4"},
 ]
 jsonschema = [
     {file = "jsonschema-3.2.0-py2.py3-none-any.whl", hash = "sha256:4e5b3cf8216f577bee9ce139cbe72eca3ea4f292ec60928ff24758ce626cd163"},
@@ -2157,7 +2149,7 @@ keyring = [
     {file = "keyring-23.0.1.tar.gz", hash = "sha256:045703609dd3fccfcdb27da201684278823b72af515aedec1a8515719a038cb8"},
 ]
 lark-parser = [
-    {file = "lark-parser-0.11.2.tar.gz", hash = "sha256:ef610461ebf2b243502f337d9d49879e39f9add846a4749e88c8dcdc1378bb6b"},
+    {file = "lark-parser-0.11.3.tar.gz", hash = "sha256:e29ca814a98bb0f81674617d878e5f611cb993c19ea47f22c80da3569425f9bd"},
 ]
 lazy-object-proxy = [
     {file = "lazy-object-proxy-1.6.0.tar.gz", hash = "sha256:489000d368377571c6f982fba6497f2aa13c6d1facc40660963da62f5c379726"},
@@ -2183,13 +2175,6 @@ lazy-object-proxy = [
     {file = "lazy_object_proxy-1.6.0-cp39-cp39-win32.whl", hash = "sha256:1fee665d2638491f4d6e55bd483e15ef21f6c8c2095f235fef72601021e64f61"},
     {file = "lazy_object_proxy-1.6.0-cp39-cp39-win_amd64.whl", hash = "sha256:f5144c75445ae3ca2057faac03fda5a902eff196702b0a24daf1d6ce0650514b"},
 ]
-livereload = [
-    {file = "livereload-2.6.3.tar.gz", hash = "sha256:776f2f865e59fde56490a56bcc6773b6917366bce0c267c60ee8aaf1a0959869"},
-]
-lunr = [
-    {file = "lunr-0.5.8-py2.py3-none-any.whl", hash = "sha256:aab3f489c4d4fab4c1294a257a30fec397db56f0a50273218ccc3efdbf01d6ca"},
-    {file = "lunr-0.5.8.tar.gz", hash = "sha256:c4fb063b98eff775dd638b3df380008ae85e6cb1d1a24d1cd81a10ef6391c26e"},
-]
 markdown = [
     {file = "Markdown-3.3.4-py3-none-any.whl", hash = "sha256:96c3ba1261de2f7547b46a00ea8463832c921d3f9d6aba3f255a6f71386db20c"},
     {file = "Markdown-3.3.4.tar.gz", hash = "sha256:31b5b491868dcc87d6c24b7e3d19a0d730d59d3e46f4eea6430a321bed387a49"},
@@ -2198,81 +2183,71 @@ markdown-include = [
     {file = "markdown-include-0.6.0.tar.gz", hash = "sha256:6f5d680e36f7780c7f0f61dca53ca581bd50d1b56137ddcd6353efafa0c3e4a2"},
 ]
 markupsafe = [
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-macosx_10_6_intel.whl", hash = "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win32.whl", hash = "sha256:b2051432115498d3562c084a49bba65d97cf251f5a331c64a12ee7e04dacc51b"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27m-win_amd64.whl", hash = "sha256:98c7086708b163d425c67c7a91bad6e466bb99d797aa64f965e9d25c12111a5e"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f"},
-    {file = "MarkupSafe-1.1.1-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-macosx_10_6_intel.whl", hash = "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_i686.whl", hash = "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-manylinux1_x86_64.whl", hash = "sha256:88e5fcfb52ee7b911e8bb6d6aa2fd21fbecc674eadd44118a9cc3863f938e735"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win32.whl", hash = "sha256:ade5e387d2ad0d7ebf59146cc00c8044acbd863725f887353a10df825fc8ae21"},
-    {file = "MarkupSafe-1.1.1-cp34-cp34m-win_amd64.whl", hash = "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-macosx_10_6_intel.whl", hash = "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:7c1699dfe0cf8ff607dbdcc1e9b9af1755371f92a68f706051cc8c37d447c905"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win32.whl", hash = "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1"},
-    {file = "MarkupSafe-1.1.1-cp35-cp35m-win_amd64.whl", hash = "sha256:9add70b36c5666a2ed02b43b335fe19002ee5235efd4b8a89bfcf9005bebac0d"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_6_intel.whl", hash = "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d53bc011414228441014aa71dbec320c66468c1030aae3a6e29778a3382d96e5"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:00bc623926325b26bb9605ae9eae8a215691f33cae5df11ca5424f06f2d1f473"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:3b8a6499709d29c2e2399569d96719a1b21dcd94410a586a18526b143ec8470f"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:84dee80c15f1b560d55bcfe6d47b27d070b4681c699c572af2e3c7cc90a3b8e0"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:b1dba4527182c95a0db8b6060cc98ac49b9e2f5e64320e2b56e47cb2831978c7"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win32.whl", hash = "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66"},
-    {file = "MarkupSafe-1.1.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b1282f8c00509d99fef04d8ba936b156d419be841854fe901d8ae224c59f0be5"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_6_intel.whl", hash = "sha256:8defac2f2ccd6805ebf65f5eeb132adcf2ab57aa11fdf4c0dd5169a004710e7d"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:bf5aa3cbcfdf57fa2ee9cd1822c862ef23037f5c832ad09cfea57fa846dec193"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:6fffc775d90dcc9aed1b89219549b329a9250d918fd0b8fa8d93d154918422e1"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:a6a744282b7718a2a62d2ed9d993cad6f5f585605ad352c11de459f4108df0a1"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:195d7d2c4fbb0ee8139a6cf67194f3973a6b3042d742ebe0a9ed36d8b6f0c07f"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win32.whl", hash = "sha256:b00c1de48212e4cc9603895652c5c410df699856a2853135b3967591e4beebc2"},
-    {file = "MarkupSafe-1.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9bf40443012702a1d2070043cb6291650a0841ece432556f784f004937f0f32c"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:acf08ac40292838b3cbbb06cfe9b2cb9ec78fce8baca31ddb87aaac2e2dc3bc2"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d9be0ba6c527163cbed5e0857c451fcd092ce83947944d6c14bc95441203f032"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:caabedc8323f1e93231b52fc32bdcde6db817623d33e100708d9a68e1f53b26b"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win32.whl", hash = "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b"},
-    {file = "MarkupSafe-1.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d73a845f227b0bfe8a7455ee623525ee656a9e2e749e4742706d80a6065d5e2c"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:98bae9582248d6cf62321dcb52aaf5d9adf0bad3b40582925ef7c7f0ed85fceb"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:2beec1e0de6924ea551859edb9e7679da6e4870d32cb766240ce17e0a0ba2014"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:7fed13866cf14bba33e7176717346713881f56d9d2bcebab207f7a036f41b850"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:6f1e273a344928347c1290119b493a1f0303c52f5a5eae5f16d74f48c15d4a85"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:feb7b34d6325451ef96bc0e36e1a6c0c1c64bc1fbec4b854f4529e51887b1621"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-win32.whl", hash = "sha256:22c178a091fc6630d0d045bdb5992d2dfe14e3259760e713c490da5323866c39"},
-    {file = "MarkupSafe-1.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:b7d644ddb4dbd407d31ffb699f1d140bc35478da613b441c582aeb7c43838dd8"},
-    {file = "MarkupSafe-1.1.1.tar.gz", hash = "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:f9081981fe268bd86831e5c75f7de206ef275defcb82bc70740ae6dc507aee51"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:0955295dd5eec6cb6cc2fe1698f4c6d84af2e92de33fbcac4111913cd100a6ff"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:0446679737af14f45767963a1a9ef7620189912317d095f2d9ffa183a4d25d2b"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_i686.whl", hash = "sha256:f826e31d18b516f653fe296d967d700fddad5901ae07c622bb3705955e1faa94"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2010_x86_64.whl", hash = "sha256:fa130dd50c57d53368c9d59395cb5526eda596d3ffe36666cd81a44d56e48872"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:905fec760bd2fa1388bb5b489ee8ee5f7291d692638ea5f67982d968366bef9f"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-win32.whl", hash = "sha256:6c4ca60fa24e85fe25b912b01e62cb969d69a23a5d5867682dd3e80b5b02581d"},
+    {file = "MarkupSafe-2.0.1-cp36-cp36m-win_amd64.whl", hash = "sha256:b2f4bf27480f5e5e8ce285a8c8fd176c0b03e93dcc6646477d4630e83440c6a9"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:0717a7390a68be14b8c793ba258e075c6f4ca819f15edfc2a3a027c823718567"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6557b31b5e2c9ddf0de32a691f2312a32f77cd7681d8af66c2692efdbef84c18"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:49e3ceeabbfb9d66c3aef5af3a60cc43b85c33df25ce03d0031a608b0a8b2e3f"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_i686.whl", hash = "sha256:d7f9850398e85aba693bb640262d3611788b1f29a79f0c93c565694658f4071f"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2010_x86_64.whl", hash = "sha256:6a7fae0dd14cf60ad5ff42baa2e95727c3d81ded453457771d02b7d2b3f9c0c2"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:b7f2d075102dc8c794cbde1947378051c4e5180d52d276987b8d28a3bd58c17d"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-win32.whl", hash = "sha256:a30e67a65b53ea0a5e62fe23682cfe22712e01f453b95233b25502f7c61cb415"},
+    {file = "MarkupSafe-2.0.1-cp37-cp37m-win_amd64.whl", hash = "sha256:611d1ad9a4288cf3e3c16014564df047fe08410e628f89805e475368bd304914"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:be98f628055368795d818ebf93da628541e10b75b41c559fdf36d104c5787066"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:1d609f577dc6e1aa17d746f8bd3c31aa4d258f4070d61b2aa5c4166c1539de35"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7d91275b0245b1da4d4cfa07e0faedd5b0812efc15b702576d103293e252af1b"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:01a9b8ea66f1658938f65b93a85ebe8bc016e6769611be228d797c9d998dd298"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:47ab1e7b91c098ab893b828deafa1203de86d0bc6ab587b160f78fe6c4011f75"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:97383d78eb34da7e1fa37dd273c20ad4320929af65d156e35a5e2d89566d9dfb"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-win32.whl", hash = "sha256:023cb26ec21ece8dc3907c0e8320058b2e0cb3c55cf9564da612bc325bed5e64"},
+    {file = "MarkupSafe-2.0.1-cp38-cp38-win_amd64.whl", hash = "sha256:984d76483eb32f1bcb536dc27e4ad56bba4baa70be32fa87152832cdd9db0833"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:2ef54abee730b502252bcdf31b10dacb0a416229b72c18b19e24a4509f273d26"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:3c112550557578c26af18a1ccc9e090bfe03832ae994343cfdacd287db6a6ae7"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_i686.whl", hash = "sha256:53edb4da6925ad13c07b6d26c2a852bd81e364f95301c66e930ab2aef5b5ddd8"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux1_x86_64.whl", hash = "sha256:f5653a225f31e113b152e56f154ccbe59eeb1c7487b39b9d9f9cdb58e6c79dc5"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4efca8f86c54b22348a5467704e3fec767b2db12fc39c6d963168ab1d3fc9135"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:ab3ef638ace319fa26553db0624c4699e31a28bb2a835c5faca8f8acf6a5a902"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:f8ba0e8349a38d3001fae7eadded3f6606f0da5d748ee53cc1dab1d6527b9509"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-win32.whl", hash = "sha256:10f82115e21dc0dfec9ab5c0223652f7197feb168c940f3ef61563fc2d6beb74"},
+    {file = "MarkupSafe-2.0.1-cp39-cp39-win_amd64.whl", hash = "sha256:693ce3f9e70a6cf7d2fb9e6c9d8b204b6b39897a2c4a1aa65728d5ac97dcc1d8"},
+    {file = "MarkupSafe-2.0.1.tar.gz", hash = "sha256:594c67807fb16238b30c44bdf74f36c02cdf22d1c8cda91ef8a0ed8dabf5620a"},
+]
+matplotlib-inline = [
+    {file = "matplotlib-inline-0.1.2.tar.gz", hash = "sha256:f41d5ff73c9f5385775d5c0bc13b424535c8402fe70ea8210f93e11f3683993e"},
+    {file = "matplotlib_inline-0.1.2-py3-none-any.whl", hash = "sha256:5cf1176f554abb4fa98cb362aa2b55c500147e4bdbb07e3fda359143e1da0811"},
 ]
 mccabe = [
     {file = "mccabe-0.6.1-py2.py3-none-any.whl", hash = "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42"},
     {file = "mccabe-0.6.1.tar.gz", hash = "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"},
 ]
+mergedeep = [
+    {file = "mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307"},
+    {file = "mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8"},
+]
 mistune = [
-    {file = "mistune-0.8.4-py2.py3-none-any.whl", hash = "sha256:88a1051873018da288eee8538d476dffe1262495144b33ecb586c4ab266bb8d4"},
-    {file = "mistune-0.8.4.tar.gz", hash = "sha256:59a3429db53c50b5c6bcc8a07f8848cb00d7dc8bdb431a4ab41920d201d4756e"},
+    {file = "mistune-2.0.0rc1-py2.py3-none-any.whl", hash = "sha256:02437870a8d594e61e4f6cff2f56f5104d17d56e3fea5fb234070ede5e7c2eae"},
+    {file = "mistune-2.0.0rc1.tar.gz", hash = "sha256:452bdba97c27efc7c1a83823ed0d6a08a9ad6ea5c67648248509f618b68aa7d9"},
 ]
 mkdocs = [
-    {file = "mkdocs-1.1.2-py3-none-any.whl", hash = "sha256:096f52ff52c02c7e90332d2e53da862fde5c062086e1b5356a6e392d5d60f5e9"},
-    {file = "mkdocs-1.1.2.tar.gz", hash = "sha256:f0b61e5402b99d7789efa032c7a74c90a20220a9c81749da06dbfbcbd52ffb39"},
+    {file = "mkdocs-1.2-py3-none-any.whl", hash = "sha256:0a194e98f7c8e83b0f538ce3d1f2a903bdc3271e2038d5032f786c7b1ebf43aa"},
+    {file = "mkdocs-1.2.tar.gz", hash = "sha256:26118adefe1d311b306f14bc2fae313cffcddc2f724d498fb40c26473c7f0017"},
 ]
 mkdocs-material = [
-    {file = "mkdocs-material-7.1.2.tar.gz", hash = "sha256:8973786f90468c1e947eb762a89a64ac749759e7dafba911636216dd7982c7e1"},
-    {file = "mkdocs_material-7.1.2-py2.py3-none-any.whl", hash = "sha256:2ac789c380bbea0801ae34e01fbf76c8bfbebcf0bce15bc76225182f1191617e"},
+    {file = "mkdocs-material-7.1.7.tar.gz", hash = "sha256:34d57af1e3e68ff4251feb82ced70545d8aa6064861ba76b1a15928399d21879"},
+    {file = "mkdocs_material-7.1.7-py2.py3-none-any.whl", hash = "sha256:1725d02efed5d989258fd1620673e78a7171f82028f30c2da8d21e7539150221"},
 ]
 mkdocs-material-extensions = [
     {file = "mkdocs-material-extensions-1.0.1.tar.gz", hash = "sha256:6947fb7f5e4291e3c61405bad3539d81e0b3cd62ae0d66ced018128af509c68f"},
     {file = "mkdocs_material_extensions-1.0.1-py3-none-any.whl", hash = "sha256:d90c807a88348aa6d1805657ec5c0b2d8d609c110e62b9dce4daf7fa981fa338"},
 ]
 mknotebooks = [
-    {file = "mknotebooks-0.7.0.post0.dev8-py3-none-any.whl", hash = "sha256:7d1bcfb109536758378f3f26a31f4bf6b092a1196c9ef1499c7e68ab396ac7dd"},
+    {file = "mknotebooks-0.7.0.post0.dev98-py3-none-any.whl", hash = "sha256:4045ed17383b94b92e531874bbd48aac055cdd756b21da43e59097e290189280"},
 ]
 mypy = [
     {file = "mypy-0.812-cp35-cp35m-macosx_10_9_x86_64.whl", hash = "sha256:a26f8ec704e5a7423c8824d425086705e381b4f1dfdef6e3a1edab7ba174ec49"},
@@ -2318,13 +2293,9 @@ nest-asyncio = [
     {file = "nest_asyncio-1.5.1-py3-none-any.whl", hash = "sha256:76d6e972265063fe92a90b9cc4fb82616e07d586b346ed9d2c89a4187acea39c"},
     {file = "nest_asyncio-1.5.1.tar.gz", hash = "sha256:afc5a1c515210a23c461932765691ad39e8eba6551c055ac8d5546e69250d0aa"},
 ]
-nltk = [
-    {file = "nltk-3.6.1-py3-none-any.whl", hash = "sha256:1235660f52ab10fda34d5277096724747f767b2903e1c0c4e14bde013552c9ba"},
-    {file = "nltk-3.6.1.zip", hash = "sha256:cbc2ed576998fcf7cd181eeb3ca029e5f0025b264074b4beb57ce780673f8b86"},
-]
 notebook = [
-    {file = "notebook-6.3.0-py3-none-any.whl", hash = "sha256:cb271af1e8134e3d6fc6d458bdc79c40cbfc84c1eb036a493f216d58f0880e92"},
-    {file = "notebook-6.3.0.tar.gz", hash = "sha256:cbc9398d6c81473e9cdb891d2cae9c0d3718fca289dda6d26df5cb660fcadc7d"},
+    {file = "notebook-6.4.0-py3-none-any.whl", hash = "sha256:f7f0a71a999c7967d9418272ae4c3378a220bd28330fbfb49860e46cf8a5838a"},
+    {file = "notebook-6.4.0.tar.gz", hash = "sha256:9c4625e2a2aa49d6eae4ce20cbc3d8976db19267e32d2a304880e0c10bf8aef9"},
 ]
 packaging = [
     {file = "packaging-20.9-py2.py3-none-any.whl", hash = "sha256:67714da7f7bc052e064859c05c595155bd1ee9f69f76557e21f051443c20947a"},
@@ -2342,8 +2313,8 @@ pathspec = [
     {file = "pathspec-0.8.1.tar.gz", hash = "sha256:86379d6b86d75816baba717e64b1a3a3469deb93bb76d613c9ce79edc5cb68fd"},
 ]
 pbr = [
-    {file = "pbr-5.5.1-py2.py3-none-any.whl", hash = "sha256:b236cde0ac9a6aedd5e3c34517b423cd4fd97ef723849da6b0d2231142d89c00"},
-    {file = "pbr-5.5.1.tar.gz", hash = "sha256:5fad80b613c402d5b7df7bd84812548b2a61e9977387a80a5fc5c396492b13c9"},
+    {file = "pbr-5.6.0-py2.py3-none-any.whl", hash = "sha256:c68c661ac5cc81058ac94247278eeda6d2e6aecb3e227b0387c30d277e7ef8d4"},
+    {file = "pbr-5.6.0.tar.gz", hash = "sha256:42df03e7797b796625b1029c0400279c7c34fd7df24a7d7818a1abb5b38710dd"},
 ]
 pexpect = [
     {file = "pexpect-4.8.0-py2.py3-none-any.whl", hash = "sha256:0b48a55dcb3c05f3329815901ea4fc1537514d6ba867a152b581d69ae3710937"},
@@ -2362,8 +2333,8 @@ pluggy = [
     {file = "pluggy-0.13.1.tar.gz", hash = "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0"},
 ]
 prometheus-client = [
-    {file = "prometheus_client-0.10.1-py2.py3-none-any.whl", hash = "sha256:030e4f9df5f53db2292eec37c6255957eb76168c6f974e4176c711cf91ed34aa"},
-    {file = "prometheus_client-0.10.1.tar.gz", hash = "sha256:b6c5a9643e3545bcbfd9451766cbaa5d9c67e7303c7bc32c750b6fa70ecb107d"},
+    {file = "prometheus_client-0.11.0-py2.py3-none-any.whl", hash = "sha256:b014bc76815eb1399da8ce5fc84b7717a3e63652b0c0f8804092c9363acab1b2"},
+    {file = "prometheus_client-0.11.0.tar.gz", hash = "sha256:3a8baade6cb80bcfe43297e33e7623f3118d660d41387593758e2fb1ea173a86"},
 ]
 prompt-toolkit = [
     {file = "prompt_toolkit-3.0.18-py3-none-any.whl", hash = "sha256:bf00f22079f5fadc949f42ae8ff7f05702826a97059ffcc6281036ad40ac6f04"},
@@ -2386,24 +2357,24 @@ pycparser = [
     {file = "pycparser-2.20.tar.gz", hash = "sha256:2d475327684562c3a96cc71adf7dc8c4f0565175cf86b6d7a404ff4c771f15f0"},
 ]
 pydocstyle = [
-    {file = "pydocstyle-6.0.0-py3-none-any.whl", hash = "sha256:d4449cf16d7e6709f63192146706933c7a334af7c0f083904799ccb851c50f6d"},
-    {file = "pydocstyle-6.0.0.tar.gz", hash = "sha256:164befb520d851dbcf0e029681b91f4f599c62c5cd8933fd54b1bfbd50e89e1f"},
+    {file = "pydocstyle-6.1.1-py3-none-any.whl", hash = "sha256:6987826d6775056839940041beef5c08cc7e3d71d63149b48e36727f70144dc4"},
+    {file = "pydocstyle-6.1.1.tar.gz", hash = "sha256:1d41b7c459ba0ee6c345f2eb9ae827cab14a7533a88c5c6f7e94923f72df92dc"},
 ]
 pyflakes = [
     {file = "pyflakes-2.3.1-py2.py3-none-any.whl", hash = "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3"},
     {file = "pyflakes-2.3.1.tar.gz", hash = "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db"},
 ]
 pygments = [
-    {file = "Pygments-2.8.1-py3-none-any.whl", hash = "sha256:534ef71d539ae97d4c3a4cf7d6f110f214b0e687e92f9cb9d2a3b0d3101289c8"},
-    {file = "Pygments-2.8.1.tar.gz", hash = "sha256:2656e1a6edcdabf4275f9a3640db59fd5de107d88e8663c5d4e9a0fa62f77f94"},
+    {file = "Pygments-2.9.0-py3-none-any.whl", hash = "sha256:d66e804411278594d764fc69ec36ec13d9ae9147193a1740cd34d272ca383b8e"},
+    {file = "Pygments-2.9.0.tar.gz", hash = "sha256:a18f47b506a429f6f4b9df81bb02beab9ca21d0a5fee38ed15aef65f0545519f"},
 ]
 pylint = [
-    {file = "pylint-2.7.4-py3-none-any.whl", hash = "sha256:209d712ec870a0182df034ae19f347e725c1e615b2269519ab58a35b3fcbbe7a"},
-    {file = "pylint-2.7.4.tar.gz", hash = "sha256:bd38914c7731cdc518634a8d3c5585951302b6e2b6de60fbb3f7a0220e21eeee"},
+    {file = "pylint-2.8.3-py3-none-any.whl", hash = "sha256:792b38ff30903884e4a9eab814ee3523731abd3c463f3ba48d7b627e87013484"},
+    {file = "pylint-2.8.3.tar.gz", hash = "sha256:0a049c5d47b629d9070c3932d13bff482b12119b6a241a93bc460b0be16953c8"},
 ]
 pymdown-extensions = [
-    {file = "pymdown-extensions-8.1.1.tar.gz", hash = "sha256:632371fa3bf1b21a0e3f4063010da59b41db049f261f4c0b0872069a9b6d1735"},
-    {file = "pymdown_extensions-8.1.1-py3-none-any.whl", hash = "sha256:478b2c04513fbb2db61688d5f6e9030a92fb9be14f1f383535c43f7be9dff95b"},
+    {file = "pymdown-extensions-8.2.tar.gz", hash = "sha256:b6daa94aad9e1310f9c64c8b1f01e4ce82937ab7eb53bfc92876a97aca02a6f4"},
+    {file = "pymdown_extensions-8.2-py3-none-any.whl", hash = "sha256:141452d8ed61165518f2c923454bf054866b85cf466feedb0eb68f04acdc2560"},
 ]
 pyparsing = [
     {file = "pyparsing-2.4.7-py2.py3-none-any.whl", hash = "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"},
@@ -2413,48 +2384,43 @@ pyrsistent = [
     {file = "pyrsistent-0.17.3.tar.gz", hash = "sha256:2e636185d9eb976a18a8a8e96efce62f2905fea90041958d8cc2a189756ebf3e"},
 ]
 pytest = [
-    {file = "pytest-6.2.3-py3-none-any.whl", hash = "sha256:6ad9c7bdf517a808242b998ac20063c41532a570d088d77eec1ee12b0b5574bc"},
-    {file = "pytest-6.2.3.tar.gz", hash = "sha256:671238a46e4df0f3498d1c3270e5deb9b32d25134c99b7d75370a68cfbe9b634"},
+    {file = "pytest-6.2.4-py3-none-any.whl", hash = "sha256:91ef2131a9bd6be8f76f1f08eac5c5317221d6ad1e143ae03894b862e8976890"},
+    {file = "pytest-6.2.4.tar.gz", hash = "sha256:50bcad0a0b9c5a72c8e4e7c9855a3ad496ca6a881a3641b4260605450772c54b"},
 ]
 pytest-cov = [
-    {file = "pytest-cov-2.11.1.tar.gz", hash = "sha256:359952d9d39b9f822d9d29324483e7ba04a3a17dd7d05aa6beb7ea01e359e5f7"},
-    {file = "pytest_cov-2.11.1-py2.py3-none-any.whl", hash = "sha256:bdb9fdb0b85a7cc825269a4c56b48ccaa5c7e365054b6038772c32ddcdc969da"},
+    {file = "pytest-cov-2.12.1.tar.gz", hash = "sha256:261ceeb8c227b726249b376b8526b600f38667ee314f910353fa318caa01f4d7"},
+    {file = "pytest_cov-2.12.1-py2.py3-none-any.whl", hash = "sha256:261bb9e47e65bd099c89c3edf92972865210c36813f80ede5277dceb77a4a62a"},
 ]
 pytest-randomly = [
-    {file = "pytest-randomly-3.7.0.tar.gz", hash = "sha256:89a4f98aed1753c447a3927b66571a796e53cdecbf671e7318f6c57d79c7f6a3"},
-    {file = "pytest_randomly-3.7.0-py3-none-any.whl", hash = "sha256:cd68c1da7d152509f59fec725f5314437727937fad31a50028693269c9261d90"},
+    {file = "pytest-randomly-3.8.0.tar.gz", hash = "sha256:d9e21a72446757129378beea00bc9a32df1fb1cfd0bbe408be1ae9685bdf1209"},
+    {file = "pytest_randomly-3.8.0-py3-none-any.whl", hash = "sha256:f5b7a09e84ee1eabcdedbb73c51d0929ae2f582bab6941dbb513bb49296d6340"},
 ]
 python-dateutil = [
     {file = "python-dateutil-2.8.1.tar.gz", hash = "sha256:73ebfe9dbf22e832286dafa60473e4cd239f8592f699aa5adaf10050e6e1823c"},
     {file = "python_dateutil-2.8.1-py2.py3-none-any.whl", hash = "sha256:75bb3f31ea686f1197762692a9ee6a7550b59fc6ca3a1f4b5d7e32fb98e2da2a"},
 ]
 pywin32 = [
-    {file = "pywin32-300-cp35-cp35m-win32.whl", hash = "sha256:1c204a81daed2089e55d11eefa4826c05e604d27fe2be40b6bf8db7b6a39da63"},
-    {file = "pywin32-300-cp35-cp35m-win_amd64.whl", hash = "sha256:350c5644775736351b77ba68da09a39c760d75d2467ecec37bd3c36a94fbed64"},
-    {file = "pywin32-300-cp36-cp36m-win32.whl", hash = "sha256:a3b4c48c852d4107e8a8ec980b76c94ce596ea66d60f7a697582ea9dce7e0db7"},
-    {file = "pywin32-300-cp36-cp36m-win_amd64.whl", hash = "sha256:27a30b887afbf05a9cbb05e3ffd43104a9b71ce292f64a635389dbad0ed1cd85"},
-    {file = "pywin32-300-cp37-cp37m-win32.whl", hash = "sha256:d7e8c7efc221f10d6400c19c32a031add1c4a58733298c09216f57b4fde110dc"},
-    {file = "pywin32-300-cp37-cp37m-win_amd64.whl", hash = "sha256:8151e4d7a19262d6694162d6da85d99a16f8b908949797fd99c83a0bfaf5807d"},
-    {file = "pywin32-300-cp38-cp38-win32.whl", hash = "sha256:fbb3b1b0fbd0b4fc2a3d1d81fe0783e30062c1abed1d17c32b7879d55858cfae"},
-    {file = "pywin32-300-cp38-cp38-win_amd64.whl", hash = "sha256:60a8fa361091b2eea27f15718f8eb7f9297e8d51b54dbc4f55f3d238093d5190"},
-    {file = "pywin32-300-cp39-cp39-win32.whl", hash = "sha256:638b68eea5cfc8def537e43e9554747f8dee786b090e47ead94bfdafdb0f2f50"},
-    {file = "pywin32-300-cp39-cp39-win_amd64.whl", hash = "sha256:b1609ce9bd5c411b81f941b246d683d6508992093203d4eb7f278f4ed1085c3f"},
+    {file = "pywin32-301-cp35-cp35m-win32.whl", hash = "sha256:93367c96e3a76dfe5003d8291ae16454ca7d84bb24d721e0b74a07610b7be4a7"},
+    {file = "pywin32-301-cp35-cp35m-win_amd64.whl", hash = "sha256:9635df6998a70282bd36e7ac2a5cef9ead1627b0a63b17c731312c7a0daebb72"},
+    {file = "pywin32-301-cp36-cp36m-win32.whl", hash = "sha256:c866f04a182a8cb9b7855de065113bbd2e40524f570db73ef1ee99ff0a5cc2f0"},
+    {file = "pywin32-301-cp36-cp36m-win_amd64.whl", hash = "sha256:dafa18e95bf2a92f298fe9c582b0e205aca45c55f989937c52c454ce65b93c78"},
+    {file = "pywin32-301-cp37-cp37m-win32.whl", hash = "sha256:98f62a3f60aa64894a290fb7494bfa0bfa0a199e9e052e1ac293b2ad3cd2818b"},
+    {file = "pywin32-301-cp37-cp37m-win_amd64.whl", hash = "sha256:fb3b4933e0382ba49305cc6cd3fb18525df7fd96aa434de19ce0878133bf8e4a"},
+    {file = "pywin32-301-cp38-cp38-win32.whl", hash = "sha256:88981dd3cfb07432625b180f49bf4e179fb8cbb5704cd512e38dd63636af7a17"},
+    {file = "pywin32-301-cp38-cp38-win_amd64.whl", hash = "sha256:8c9d33968aa7fcddf44e47750e18f3d034c3e443a707688a008a2e52bbef7e96"},
+    {file = "pywin32-301-cp39-cp39-win32.whl", hash = "sha256:595d397df65f1b2e0beaca63a883ae6d8b6df1cdea85c16ae85f6d2e648133fe"},
+    {file = "pywin32-301-cp39-cp39-win_amd64.whl", hash = "sha256:87604a4087434cd814ad8973bd47d6524bd1fa9e971ce428e76b62a5e0860fdf"},
 ]
 pywin32-ctypes = [
     {file = "pywin32-ctypes-0.2.0.tar.gz", hash = "sha256:24ffc3b341d457d48e8922352130cf2644024a4ff09762a2261fd34c36ee5942"},
     {file = "pywin32_ctypes-0.2.0-py2.py3-none-any.whl", hash = "sha256:9dc2d991b3479cc2df15930958b674a48a227d5361d413827a4cfd0b5876fc98"},
 ]
 pywinpty = [
-    {file = "pywinpty-0.5.7-cp27-cp27m-win32.whl", hash = "sha256:b358cb552c0f6baf790de375fab96524a0498c9df83489b8c23f7f08795e966b"},
-    {file = "pywinpty-0.5.7-cp27-cp27m-win_amd64.whl", hash = "sha256:1e525a4de05e72016a7af27836d512db67d06a015aeaf2fa0180f8e6a039b3c2"},
-    {file = "pywinpty-0.5.7-cp35-cp35m-win32.whl", hash = "sha256:2740eeeb59297593a0d3f762269b01d0285c1b829d6827445fcd348fb47f7e70"},
-    {file = "pywinpty-0.5.7-cp35-cp35m-win_amd64.whl", hash = "sha256:33df97f79843b2b8b8bc5c7aaf54adec08cc1bae94ee99dfb1a93c7a67704d95"},
-    {file = "pywinpty-0.5.7-cp36-cp36m-win32.whl", hash = "sha256:e854211df55d107f0edfda8a80b39dfc87015bef52a8fe6594eb379240d81df2"},
-    {file = "pywinpty-0.5.7-cp36-cp36m-win_amd64.whl", hash = "sha256:dbd838de92de1d4ebf0dce9d4d5e4fc38d0b7b1de837947a18b57a882f219139"},
-    {file = "pywinpty-0.5.7-cp37-cp37m-win32.whl", hash = "sha256:5fb2c6c6819491b216f78acc2c521b9df21e0f53b9a399d58a5c151a3c4e2a2d"},
-    {file = "pywinpty-0.5.7-cp37-cp37m-win_amd64.whl", hash = "sha256:dd22c8efacf600730abe4a46c1388355ce0d4ab75dc79b15d23a7bd87bf05b48"},
-    {file = "pywinpty-0.5.7-cp38-cp38-win_amd64.whl", hash = "sha256:8fc5019ff3efb4f13708bd3b5ad327589c1a554cb516d792527361525a7cb78c"},
-    {file = "pywinpty-0.5.7.tar.gz", hash = "sha256:2d7e9c881638a72ffdca3f5417dd1563b60f603e1b43e5895674c2a1b01f95a0"},
+    {file = "pywinpty-1.1.1-cp36-none-win_amd64.whl", hash = "sha256:fa2a0af28eaaacc59227c6edbc0f1525704d68b2dfa3e5b47ae21c5aa25d6d78"},
+    {file = "pywinpty-1.1.1-cp37-none-win_amd64.whl", hash = "sha256:0fe3f538860c6b06e6fbe63da0ee5dab5194746b0df1be7ed65b4fce5da21d21"},
+    {file = "pywinpty-1.1.1-cp38-none-win_amd64.whl", hash = "sha256:12c89765b3102d2eea3d39d191d1b0baea68fb5e3bd094c67b2575b3c9ebfa12"},
+    {file = "pywinpty-1.1.1-cp39-none-win_amd64.whl", hash = "sha256:50bce6f7d9857ffe9694847af7e8bf989b198d0ebc2bf30e26d54c4622cb5c50"},
+    {file = "pywinpty-1.1.1.tar.gz", hash = "sha256:4a3ffa2444daf15c5f65a76b5b2864447cc915564e41e2876816b9e4fe849070"},
 ]
 pyyaml = [
     {file = "PyYAML-5.4.1-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:3b2b1824fe7112845700f815ff6a489360226a5609b96ec2190a45e62a9fc922"},
@@ -2487,43 +2453,47 @@ pyyaml = [
     {file = "PyYAML-5.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:c20cfa2d49991c8b4147af39859b167664f2ad4561704ee74c1de03318e898db"},
     {file = "PyYAML-5.4.1.tar.gz", hash = "sha256:607774cbba28732bfa802b54baa7484215f530991055bb562efbed5b2f20a45e"},
 ]
+pyyaml-env-tag = [
+    {file = "pyyaml_env_tag-0.1-py3-none-any.whl", hash = "sha256:af31106dec8a4d68c60207c1886031cbf839b68aa7abccdb19868200532c2069"},
+    {file = "pyyaml_env_tag-0.1.tar.gz", hash = "sha256:70092675bda14fdec33b31ba77e7543de9ddc88f2e5b99160396572d11525bdb"},
+]
 pyzmq = [
-    {file = "pyzmq-22.0.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:c0cde362075ee8f3d2b0353b283e203c2200243b5a15d5c5c03b78112a17e7d4"},
-    {file = "pyzmq-22.0.3-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:ff1ea14075bbddd6f29bf6beb8a46d0db779bcec6b9820909584081ec119f8fd"},
-    {file = "pyzmq-22.0.3-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:26380487eae4034d6c2a3fb8d0f2dff6dd0d9dd711894e8d25aa2d1938950a33"},
-    {file = "pyzmq-22.0.3-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:3e29f9cf85a40d521d048b55c63f59d6c772ac1c4bf51cdfc23b62a62e377c33"},
-    {file = "pyzmq-22.0.3-cp36-cp36m-win32.whl", hash = "sha256:4f34a173f813b38b83f058e267e30465ed64b22cd0cf6bad21148d3fa718f9bb"},
-    {file = "pyzmq-22.0.3-cp36-cp36m-win_amd64.whl", hash = "sha256:30df70f81fe210506aa354d7fd486a39b87d9f7f24c3d3f4f698ec5d96b8c084"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:7026f0353977431fc884abd4ac28268894bd1a780ba84bb266d470b0ec26d2ed"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:6d4163704201fff0f3ab0cd5d7a0ea1514ecfffd3926d62ec7e740a04d2012c7"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:763c175294d861869f18eb42901d500eda7d3fa4565f160b3b2fd2678ea0ebab"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:61e4bb6cd60caf1abcd796c3f48395e22c5b486eeca6f3a8797975c57d94b03e"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-win32.whl", hash = "sha256:b25e5d339550a850f7e919fe8cb4c8eabe4c917613db48dab3df19bfb9a28969"},
-    {file = "pyzmq-22.0.3-cp37-cp37m-win_amd64.whl", hash = "sha256:3ef50d74469b03725d781a2a03c57537d86847ccde587130fe35caafea8f75c6"},
-    {file = "pyzmq-22.0.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:60e63577b85055e4cc43892fecd877b86695ee3ef12d5d10a3c5d6e77a7cc1a3"},
-    {file = "pyzmq-22.0.3-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:f5831eff6b125992ec65d973f5151c48003b6754030094723ac4c6e80a97c8c4"},
-    {file = "pyzmq-22.0.3-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:9221783dacb419604d5345d0e097bddef4459a9a95322de6c306bf1d9896559f"},
-    {file = "pyzmq-22.0.3-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:b62ea18c0458a65ccd5be90f276f7a5a3f26a6dea0066d948ce2fa896051420f"},
-    {file = "pyzmq-22.0.3-cp38-cp38-win32.whl", hash = "sha256:81e7df0da456206201e226491aa1fc449da85328bf33bbeec2c03bb3a9f18324"},
-    {file = "pyzmq-22.0.3-cp38-cp38-win_amd64.whl", hash = "sha256:f52070871a0fd90a99130babf21f8af192304ec1e995bec2a9533efc21ea4452"},
-    {file = "pyzmq-22.0.3-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:c5e29fe4678f97ce429f076a2a049a3d0b2660ada8f2c621e5dc9939426056dd"},
-    {file = "pyzmq-22.0.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d18ddc6741b51f3985978f2fda57ddcdae359662d7a6b395bc8ff2292fca14bd"},
-    {file = "pyzmq-22.0.3-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:4231943514812dfb74f44eadcf85e8dd8cf302b4d0bce450ce1357cac88dbfdc"},
-    {file = "pyzmq-22.0.3-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:23a74de4b43c05c3044aeba0d1f3970def8f916151a712a3ac1e5cd9c0bc2902"},
-    {file = "pyzmq-22.0.3-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:532af3e6dddea62d9c49062ece5add998c9823c2419da943cf95589f56737de0"},
-    {file = "pyzmq-22.0.3-cp39-cp39-win32.whl", hash = "sha256:33acd2b9790818b9d00526135acf12790649d8d34b2b04d64558b469c9d86820"},
-    {file = "pyzmq-22.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:a558c5bc89d56d7253187dccc4e81b5bb0eac5ae9511eb4951910a1245d04622"},
-    {file = "pyzmq-22.0.3-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:581787c62eaa0e0db6c5413cedc393ebbadac6ddfd22e1cf9a60da23c4f1a4b2"},
-    {file = "pyzmq-22.0.3-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:38e3dca75d81bec4f2defa14b0a65b74545812bb519a8e89c8df96bbf4639356"},
-    {file = "pyzmq-22.0.3-pp36-pypy36_pp73-win32.whl", hash = "sha256:2f971431aaebe0a8b54ac018e041c2f0b949a43745444e4dadcc80d0f0ef8457"},
-    {file = "pyzmq-22.0.3-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:da7d4d4c778c86b60949d17531e60c54ed3726878de8a7f8a6d6e7f8cc8c3205"},
-    {file = "pyzmq-22.0.3-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:13465c1ff969cab328bc92f7015ce3843f6e35f8871ad79d236e4fbc85dbe4cb"},
-    {file = "pyzmq-22.0.3-pp37-pypy37_pp73-win32.whl", hash = "sha256:279cc9b51db48bec2db146f38e336049ac5a59e5f12fb3a8ad864e238c1c62e3"},
-    {file = "pyzmq-22.0.3.tar.gz", hash = "sha256:f7f63ce127980d40f3e6a5fdb87abf17ce1a7c2bd8bf2c7560e1bbce8ab1f92d"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4e9b9a2f6944acdaf57316436c1acdcb30b8df76726bcf570ad9342bc5001654"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:24fb5bb641f0b2aa25fc3832f4b6fc62430f14a7d328229fe994b2bcdc07c93a"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:c4674004ed64685a38bee222cd75afa769424ec603f9329f0dd4777138337f48"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-manylinux2014_aarch64.whl", hash = "sha256:461ed80d741692d9457ab820b1cc057ba9c37c394e67b647b639f623c8b321f6"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-win32.whl", hash = "sha256:de5806be66c9108e4dcdaced084e8ceae14100aa559e2d57b4f0cceb98c462de"},
+    {file = "pyzmq-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a1c77796f395804d6002ff56a6a8168c1f98579896897ad7e35665a9b4a9eec5"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:c6a81c9e6754465d09a87e3acd74d9bb1f0039b2d785c6899622f0afdb41d760"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:0f0f27eaab9ba7b92d73d71c51d1a04464a1da6097a252d007922103253d2313"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:4b8fb1b3174b56fd020e4b10232b1764e52cf7f3babcfb460c5253bdc48adad0"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-manylinux2014_aarch64.whl", hash = "sha256:c8fff75af4c7af92dce9f81fa2a83ed009c3e1f33ee8b5222db2ef80b94e242e"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-win32.whl", hash = "sha256:cb9f9fe1305ef69b65794655fd89b2209b11bff3e837de981820a8aa051ef914"},
+    {file = "pyzmq-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:bf80b2cec42d96117248b99d3c86e263a00469c840a778e6cb52d916f4fdf82c"},
+    {file = "pyzmq-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0ea7f4237991b0f745a4432c63e888450840bf8cb6c48b93fb7d62864f455529"},
+    {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_i686.whl", hash = "sha256:12ffcf33db6ba7c0e5aaf901e65517f5e2b719367b80bcbfad692f546a297c7a"},
+    {file = "pyzmq-22.1.0-cp38-cp38-manylinux2010_x86_64.whl", hash = "sha256:d3ecfee2ee8d91ab2e08d2d8e89302c729b244e302bbc39c5b5dde42306ff003"},
+    {file = "pyzmq-22.1.0-cp38-cp38-manylinux2014_aarch64.whl", hash = "sha256:68e2c4505992ab5b89f976f89a9135742b18d60068f761bef994a6805f1cae0c"},
+    {file = "pyzmq-22.1.0-cp38-cp38-win32.whl", hash = "sha256:285514956c08c7830da9d94e01f5414661a987831bd9f95e4d89cc8aaae8da10"},
+    {file = "pyzmq-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:d5e5be93e1714a59a535bbbc086b9e4fd2448c7547c5288548f6fd86353cad9e"},
+    {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b2f707b52e09098a7770503e39294ca6e22ae5138ffa1dd36248b6436d23d78e"},
+    {file = "pyzmq-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:18dd2ca4540c476558099891c129e6f94109971d110b549db2a9775c817cedbd"},
+    {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_i686.whl", hash = "sha256:c6d0c32532a0519997e1ded767e184ebb8543bdb351f8eff8570bd461e874efc"},
+    {file = "pyzmq-22.1.0-cp39-cp39-manylinux2010_x86_64.whl", hash = "sha256:9ee48413a2d3cd867fd836737b4c89c24cea1150a37f4856d82d20293fa7519f"},
+    {file = "pyzmq-22.1.0-cp39-cp39-manylinux2014_aarch64.whl", hash = "sha256:4c4fe69c7dc0d13d4ae180ad650bb900854367f3349d3c16f0569f6c6447f698"},
+    {file = "pyzmq-22.1.0-cp39-cp39-win32.whl", hash = "sha256:fc712a90401bcbf3fa25747f189d6dcfccbecc32712701cad25c6355589dac57"},
+    {file = "pyzmq-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:68be16107f41563b9f67d93dff1c9f5587e0f76aa8fd91dc04c83d813bcdab1f"},
+    {file = "pyzmq-22.1.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:734ea6565c71fc2d03d5b8c7d0d7519c96bb5567e0396da1b563c24a4ac66f0c"},
+    {file = "pyzmq-22.1.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:1389b615917d4196962a9b469e947ba862a8ec6f5094a47da5e7a8d404bc07a4"},
+    {file = "pyzmq-22.1.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:41049cff5265e9cd75606aa2c90a76b9c80b98d8fe70ee08cf4af3cedb113358"},
+    {file = "pyzmq-22.1.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:f49755684a963731479ff3035d45a8185545b4c9f662d368bd349c419839886d"},
+    {file = "pyzmq-22.1.0-pp37-pypy37_pp73-manylinux2010_x86_64.whl", hash = "sha256:6355f81947e1fe6e7bb9e123aeb3067264391d3ebe8402709f824ef8673fa6f3"},
+    {file = "pyzmq-22.1.0-pp37-pypy37_pp73-win32.whl", hash = "sha256:089b974ec04d663b8685ac90e86bfe0e4da9d911ff3cf52cb765ff22408b102d"},
+    {file = "pyzmq-22.1.0.tar.gz", hash = "sha256:7040d6dd85ea65703904d023d7f57fab793d7ffee9ba9e14f3b897f34ff2415d"},
 ]
 qtconsole = [
-    {file = "qtconsole-5.0.3-py3-none-any.whl", hash = "sha256:4a38053993ca2da058f76f8d75b3d8906efbf9183de516f92f222ac8e37d9614"},
-    {file = "qtconsole-5.0.3.tar.gz", hash = "sha256:c091a35607d2a2432e004c4a112d241ce908086570cf68594176dd52ccaa212d"},
+    {file = "qtconsole-5.1.0-py3-none-any.whl", hash = "sha256:3a2adecc43ff201a08972fb2179df22e7b3a08d71b9ed680f46ad1bfd4fb9132"},
+    {file = "qtconsole-5.1.0.tar.gz", hash = "sha256:12c734494901658787339dea9bbd82f3dc0d5e394071377a1c77b4a0954d7d8b"},
 ]
 qtpy = [
     {file = "QtPy-1.9.0-py2.py3-none-any.whl", hash = "sha256:fa0b8363b363e89b2a6f49eddc162a04c0699ae95e109a6be3bb145a913190ea"},
@@ -2585,8 +2555,8 @@ requests-toolbelt = [
     {file = "requests_toolbelt-0.9.1-py2.py3-none-any.whl", hash = "sha256:380606e1d10dc85c3bd47bf5a6095f815ec007be7a8b69c878507068df059e6f"},
 ]
 rfc3986 = [
-    {file = "rfc3986-1.4.0-py2.py3-none-any.whl", hash = "sha256:af9147e9aceda37c91a05f4deb128d4b4b49d6b199775fd2d2927768abdc8f50"},
-    {file = "rfc3986-1.4.0.tar.gz", hash = "sha256:112398da31a3344dc25dbf477d8df6cb34f9278a94fee2625d89e4514be8bb9d"},
+    {file = "rfc3986-1.5.0-py2.py3-none-any.whl", hash = "sha256:a86d6e1f5b1dc238b218b012df0aa79409667bb209e58da56d0b94704e712a97"},
+    {file = "rfc3986-1.5.0.tar.gz", hash = "sha256:270aaf10d87d0d4e095063c65bf3ddbc6ee3d0b226328ce21e036f946e421835"},
 ]
 safety = [
     {file = "safety-1.10.3-py2.py3-none-any.whl", hash = "sha256:5f802ad5df5614f9622d8d71fedec2757099705c2356f862847c58c6dfe13e84"},
@@ -2601,8 +2571,8 @@ send2trash = [
     {file = "Send2Trash-1.5.0.tar.gz", hash = "sha256:60001cc07d707fe247c94f74ca6ac0d3255aabcb930529690897ca2a39db28b2"},
 ]
 six = [
-    {file = "six-1.15.0-py2.py3-none-any.whl", hash = "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"},
-    {file = "six-1.15.0.tar.gz", hash = "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259"},
+    {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
+    {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"},
 ]
 smmap = [
     {file = "smmap-4.0.0-py2.py3-none-any.whl", hash = "sha256:a9a7479e4c572e2e775c404dcd3080c8dc49f39918c2cf74913d30c4c478e3c2"},
@@ -2613,24 +2583,24 @@ snowballstemmer = [
     {file = "snowballstemmer-2.1.0.tar.gz", hash = "sha256:e997baa4f2e9139951b6f4c631bad912dfd3c792467e2f03d7239464af90e914"},
 ]
 sortedcontainers = [
-    {file = "sortedcontainers-2.3.0-py2.py3-none-any.whl", hash = "sha256:37257a32add0a3ee490bb170b599e93095eed89a55da91fa9f48753ea12fd73f"},
-    {file = "sortedcontainers-2.3.0.tar.gz", hash = "sha256:59cc937650cf60d677c16775597c89a960658a09cf7c1a668f86e1e4464b10a1"},
+    {file = "sortedcontainers-2.4.0-py2.py3-none-any.whl", hash = "sha256:a163dcaede0f1c021485e957a39245190e74249897e2ae4b2aa38595db237ee0"},
+    {file = "sortedcontainers-2.4.0.tar.gz", hash = "sha256:25caa5a06cc30b6b83d11423433f65d1f9d76c4c6a0c90e3379eaa43b9bfdb88"},
 ]
 stevedore = [
     {file = "stevedore-3.3.0-py3-none-any.whl", hash = "sha256:50d7b78fbaf0d04cd62411188fa7eedcb03eb7f4c4b37005615ceebe582aa82a"},
     {file = "stevedore-3.3.0.tar.gz", hash = "sha256:3a5bbd0652bf552748871eaa73a4a8dc2899786bc497a2aa1fcb4dcdb0debeee"},
 ]
 terminado = [
-    {file = "terminado-0.9.4-py3-none-any.whl", hash = "sha256:daed77f9fad7b32558fa84b226a76f45a02242c20813502f36c4e1ade6d8f1ad"},
-    {file = "terminado-0.9.4.tar.gz", hash = "sha256:9a7dbcfbc2778830eeb70261bf7aa9d98a3eac8631a3afe3febeb57c12f798be"},
+    {file = "terminado-0.10.0-py3-none-any.whl", hash = "sha256:048ce7b271ad1f94c48130844af1de163e54913b919f8c268c89b36a6d468d7c"},
+    {file = "terminado-0.10.0.tar.gz", hash = "sha256:46fd07c9dc7db7321922270d544a1f18eaa7a02fd6cd4438314f27a687cabbea"},
 ]
 testfixtures = [
     {file = "testfixtures-6.17.1-py2.py3-none-any.whl", hash = "sha256:9ed31e83f59619e2fa17df053b241e16e0608f4580f7b5a9333a0c9bdcc99137"},
     {file = "testfixtures-6.17.1.tar.gz", hash = "sha256:5ec3a0dd6f71cc4c304fbc024a10cc293d3e0b852c868014b9f233203e149bda"},
 ]
 testpath = [
-    {file = "testpath-0.4.4-py2.py3-none-any.whl", hash = "sha256:bfcf9411ef4bf3db7579063e0546938b1edda3d69f4e1fb8756991f5951f85d4"},
-    {file = "testpath-0.4.4.tar.gz", hash = "sha256:60e0a3261c149755f4399a1fff7d37523179a70fdc3abdf78de9fc2604aeec7e"},
+    {file = "testpath-0.5.0-py3-none-any.whl", hash = "sha256:8044f9a0bab6567fc644a3593164e872543bb44225b0e24846e2c89237937589"},
+    {file = "testpath-0.5.0.tar.gz", hash = "sha256:1acf7a0bcd3004ae8357409fc33751e16d37ccc650921da1094a86581ad1e417"},
 ]
 toml = [
     {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
@@ -2680,12 +2650,12 @@ tornado = [
     {file = "tornado-6.1.tar.gz", hash = "sha256:33c6e81d7bd55b468d2e793517c909b139960b6c790a60b7991b9b6b76fb9791"},
 ]
 tox = [
-    {file = "tox-3.23.0-py2.py3-none-any.whl", hash = "sha256:e007673f3595cede9b17a7c4962389e4305d4a3682a6c5a4159a1453b4f326aa"},
-    {file = "tox-3.23.0.tar.gz", hash = "sha256:05a4dbd5e4d3d8269b72b55600f0b0303e2eb47ad5c6fe76d3576f4c58d93661"},
+    {file = "tox-3.23.1-py2.py3-none-any.whl", hash = "sha256:b0b5818049a1c1997599d42012a637a33f24c62ab8187223fdd318fa8522637b"},
+    {file = "tox-3.23.1.tar.gz", hash = "sha256:307a81ddb82bd463971a273f33e9533a24ed22185f27db8ce3386bff27d324e3"},
 ]
 tqdm = [
-    {file = "tqdm-4.60.0-py2.py3-none-any.whl", hash = "sha256:daec693491c52e9498632dfbe9ccfc4882a557f5fa08982db1b4d3adbe0887c3"},
-    {file = "tqdm-4.60.0.tar.gz", hash = "sha256:ebdebdb95e3477ceea267decfc0784859aa3df3e27e22d23b83e9b272bf157ae"},
+    {file = "tqdm-4.61.0-py2.py3-none-any.whl", hash = "sha256:736524215c690621b06fc89d0310a49822d75e599fcd0feb7cc742b98d692493"},
+    {file = "tqdm-4.61.0.tar.gz", hash = "sha256:cd5791b5d7c3f2f1819efc81d36eb719a38e0906a7380365c556779f585ea042"},
 ]
 traitlets = [
     {file = "traitlets-5.0.5-py3-none-any.whl", hash = "sha256:69ff3f9d5351f31a7ad80443c2674b7099df13cc41fc5fa6e2f6d3b0330b0426"},
@@ -2728,22 +2698,41 @@ typed-ast = [
     {file = "typed_ast-1.4.3.tar.gz", hash = "sha256:fb1bbeac803adea29cedd70781399c99138358c26d05fcbd23c13016b7f5ec65"},
 ]
 typing-extensions = [
-    {file = "typing_extensions-3.7.4.3-py2-none-any.whl", hash = "sha256:dafc7639cde7f1b6e1acc0f457842a83e722ccca8eef5270af2d74792619a89f"},
-    {file = "typing_extensions-3.7.4.3-py3-none-any.whl", hash = "sha256:7cb407020f00f7bfc3cb3e7881628838e69d8f3fcab2f64742a5e76b2f841918"},
-    {file = "typing_extensions-3.7.4.3.tar.gz", hash = "sha256:99d4073b617d30288f569d3f13d2bd7548c3a7e4c8de87db09a9d29bb3a4a60c"},
+    {file = "typing_extensions-3.10.0.0-py2-none-any.whl", hash = "sha256:0ac0f89795dd19de6b97debb0c6af1c70987fd80a2d62d1958f7e56fcc31b497"},
+    {file = "typing_extensions-3.10.0.0-py3-none-any.whl", hash = "sha256:779383f6086d90c99ae41cf0ff39aac8a7937a9283ce0a414e5dd782f4c94a84"},
+    {file = "typing_extensions-3.10.0.0.tar.gz", hash = "sha256:50b6f157849174217d0656f99dc82fe932884fb250826c18350e159ec6cdf342"},
 ]
 urllib3 = [
-    {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
-    {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
+    {file = "urllib3-1.26.5-py2.py3-none-any.whl", hash = "sha256:753a0374df26658f99d826cfe40394a686d05985786d946fbe4165b5148f5a7c"},
+    {file = "urllib3-1.26.5.tar.gz", hash = "sha256:a7acd0977125325f516bda9735fa7142b909a8d01e8b2e4c8108d0984e6e0098"},
 ]
 virtualenv = [
-    {file = "virtualenv-20.4.3-py2.py3-none-any.whl", hash = "sha256:83f95875d382c7abafe06bd2a4cdd1b363e1bb77e02f155ebe8ac082a916b37c"},
-    {file = "virtualenv-20.4.3.tar.gz", hash = "sha256:49ec4eb4c224c6f7dd81bb6d0a28a09ecae5894f4e593c89b0db0885f565a107"},
+    {file = "virtualenv-20.4.7-py2.py3-none-any.whl", hash = "sha256:2b0126166ea7c9c3661f5b8e06773d28f83322de7a3ff7d06f0aed18c9de6a76"},
+    {file = "virtualenv-20.4.7.tar.gz", hash = "sha256:14fdf849f80dbb29a4eb6caa9875d476ee2a5cf76a5f5415fa2f1606010ab467"},
 ]
 vulture = [
     {file = "vulture-2.3-py2.py3-none-any.whl", hash = "sha256:f39de5e6f1df1f70c3b50da54f1c8d494159e9ca3d01a9b89eac929600591703"},
     {file = "vulture-2.3.tar.gz", hash = "sha256:03d5a62bcbe9ceb9a9b0575f42d71a2d414070229f2e6f95fa6e7c71aaaed967"},
 ]
+watchdog = [
+    {file = "watchdog-2.1.2-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:581e3548159fe7d2a9f377a1fbcb41bdcee46849cca8ab803c7ac2e5e04ec77c"},
+    {file = "watchdog-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:edcd9ef3fd460bb8a98eb1fcf99941e9fd9f275f45f1a82cb1359ec92975d647"},
+    {file = "watchdog-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:d34ce2261f118ecd57eedeef95fc2a495fc4a40b3ed7b3bf0bd7a8ccc1ab4f8f"},
+    {file = "watchdog-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:668391e6c32742d76e5be5db6bf95c455fa4b3d11e76a77c13b39bccb3a47a72"},
+    {file = "watchdog-2.1.2-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:6ef9fe57162c4c361692620e1d9167574ba1975ee468b24051ca11c9bba6438e"},
+    {file = "watchdog-2.1.2-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:58ebb1095ee493008a7789d47dd62e4999505d82be89fc884d473086fccc6ebd"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:91387ee2421f30b75f7ff632c9d48f76648e56bf346a7c805c0a34187a93aab4"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:a6471517315a8541a943c00b45f1d252e36898a3ae963d2d52509b89a50cb2b9"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_i686.whl", hash = "sha256:a42e6d652f820b2b94cd03156c62559a2ea68d476476dfcd77d931e7f1012d4a"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:3d6405681471ebe0beb3aa083998c4870e48b57f8afdb45ea1b5957cc5cf1014"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:598d772beeaf9c98d0df946fbabf0c8365dd95ea46a250c224c725fe0c4730bc"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:4b219d46d89cfa49af1d73175487c14a318a74cb8c5442603fd13c6a5b418c86"},
+    {file = "watchdog-2.1.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:188145185c08c73c56f1478ccf1f0f0f85101191439679b35b6b100886ce0b39"},
+    {file = "watchdog-2.1.2-py3-none-win32.whl", hash = "sha256:255a32d44bbbe62e52874ff755e2eefe271b150e0ec240ad7718a62a7a7a73c4"},
+    {file = "watchdog-2.1.2-py3-none-win_amd64.whl", hash = "sha256:1a62a4671796dc93d1a7262286217d9e75823c63d4c42782912d39a506d30046"},
+    {file = "watchdog-2.1.2-py3-none-win_ia64.whl", hash = "sha256:104266a778906ae0e971368d368a65c4cd032a490a9fca5ba0b78c6c7ae11720"},
+    {file = "watchdog-2.1.2.tar.gz", hash = "sha256:0237db4d9024859bea27d0efb59fe75eef290833fd988b8ead7a879b0308c2db"},
+]
 wcwidth = [
     {file = "wcwidth-0.2.5-py2.py3-none-any.whl", hash = "sha256:beb4802a9cebb9144e99086eff703a642a13d6a0052920003a230f3294bbe784"},
     {file = "wcwidth-0.2.5.tar.gz", hash = "sha256:c4d647b99872929fdb7bdcaa4fbe7f01413ed3d98077df798530e5b04f116c83"},
diff --git a/pylogics/__init__.py b/pylogics/__init__.py
index 51e41f4..fb88c2d 100644
--- a/pylogics/__init__.py
+++ b/pylogics/__init__.py
@@ -22,4 +22,4 @@
 
 """A Python library for logic formalisms representation and manipulation."""
 
-__version__ = "0.1.0.alpha0"
+__version__ = "0.1.0"
diff --git a/pylogics/parsers/ldl.lark b/pylogics/parsers/ldl.lark
index 3b85281..02a2432 100644
--- a/pylogics/parsers/ldl.lark
+++ b/pylogics/parsers/ldl.lark
@@ -1,31 +1,25 @@
 start: ldlf_formula
 
-?ldlf_formula:  ldlf_equivalence
+?ldlf_formula:     ldlf_equivalence
 ?ldlf_equivalence: ldlf_implication (EQUIVALENCE ldlf_implication)*
 ?ldlf_implication: ldlf_or (IMPLY ldlf_or)*
-?ldlf_or: ldlf_and (OR ldlf_and)*
-?ldlf_and: ldlf_unaryop (AND ldlf_unaryop)*
+?ldlf_or:          ldlf_and (OR ldlf_and)*
+?ldlf_and:         ldlf_unaryop (AND ldlf_unaryop)*
 
 ?ldlf_unaryop:     ldlf_box
              |     ldlf_diamond
              |     ldlf_not
              |     ldlf_wrapped
-?ldlf_box: BOXLSEPARATOR regular_expression BOXRSEPARATOR ldlf_unaryop
-?ldlf_diamond: DIAMONDLSEPARATOR regular_expression DIAMONDRSEPARATOR ldlf_unaryop
-?ldlf_not: NOT ldlf_unaryop
+?ldlf_box:         LEFT_SQUARE_BRACKET regular_expression RIGHT_SQUARE_BRACKET ldlf_unaryop
+?ldlf_diamond:     LEFT_ANGLE_BRACKET regular_expression RIGHT_ANGLE_BRACKET ldlf_unaryop
+?ldlf_not:         NOT ldlf_unaryop
 ?ldlf_wrapped:    ldlf_atom
-             |    LSEPARATOR ldlf_formula RSEPARATOR
-?ldlf_atom:   ldlf_tt
-          |   ldlf_ff
-          |   ldlf_last
-          |   ldlf_end
-          |   ldlf_prop_true
-          |   ldlf_prop_false
-          |   ldlf_prop_atom
+             |    LEFT_PARENTHESIS ldlf_formula RIGHT_PARENTHESIS
+?ldlf_atom:       ldlf_tt
+          |       ldlf_ff
+          |       ldlf_last
+          |       ldlf_end
 
-ldlf_prop_true: TRUE
-ldlf_prop_false: FALSE
-ldlf_prop_atom: SYMBOL_NAME
 
 ldlf_tt: TT
 ldlf_ff: FF
@@ -37,23 +31,21 @@ regular_expression: re_union
 ?re_union:      re_sequence (UNION re_sequence)*
 ?re_sequence:   re_star (SEQ re_star)*
 ?re_star:       re_test STAR?
-?re_test:       TEST ldlf_formula
+?re_test:       ldlf_formula TEST
         |       re_wrapped
 ?re_wrapped:    re_propositional
-           |    LSEPARATOR regular_expression RSEPARATOR
+           |    LEFT_PARENTHESIS regular_expression RIGHT_PARENTHESIS
 re_propositional: propositional_formula
 
 
-BOXLSEPARATOR: "["
-BOXRSEPARATOR: "]"
-DIAMONDLSEPARATOR: "<"
-DIAMONDRSEPARATOR: ">"
+LEFT_SQUARE_BRACKET: "["
+RIGHT_SQUARE_BRACKET: "]"
+LEFT_ANGLE_BRACKET: "<"
+RIGHT_ANGLE_BRACKET: ">"
 UNION: "+"
 SEQ: ";"
 TEST: "?"
 STAR: "*"
-TT.2: /(?i:tt)/
-FF.2: /(?i:ff)/
 
 %ignore /\s+/
 
@@ -66,7 +58,9 @@ FF.2: /(?i:ff)/
 %import  .pl.OR -> OR
 %import  .pl.AND -> AND
 %import  .pl.NOT -> NOT
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
-%import .ltl.LAST -> LAST
-%import .ltl.END -> END
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
+%import  .ltl.LAST -> LAST
+%import  .ltl.END -> END
+%import  .ltl.TT -> TT
+%import  .ltl.FF -> FF
diff --git a/pylogics/parsers/ldl.py b/pylogics/parsers/ldl.py
index 049acaf..0e76e62 100644
--- a/pylogics/parsers/ldl.py
+++ b/pylogics/parsers/ldl.py
@@ -31,23 +31,12 @@
     FalseFormula,
     Formula,
     Implies,
+    Logic,
     Not,
     Or,
     TrueFormula,
 )
-from pylogics.syntax.ldl import (
-    Box,
-    Diamond,
-    End,
-    Last,
-    LDLFalse,
-    LDLTrue,
-    Prop,
-    Seq,
-    Star,
-    Test,
-    Union,
-)
+from pylogics.syntax.ldl import Box, Diamond, End, Last, Prop, Seq, Star, Test, Union
 from pylogics.syntax.pl import Atomic
 
 
@@ -123,11 +112,11 @@ def ldlf_atom(cls, args):
 
     @classmethod
     def ldlf_tt(cls, args):
-        return LDLTrue()
+        return TrueFormula(logic=Logic.LDL)
 
     @classmethod
     def ldlf_ff(cls, args):
-        return LDLFalse()
+        return FalseFormula(logic=Logic.LDL)
 
     @classmethod
     def ldlf_last(cls, args):
@@ -139,18 +128,18 @@ def ldlf_end(cls, args):
 
     @classmethod
     def ldlf_prop_true(cls, args):
-        return Diamond(Prop(TrueFormula()), LDLTrue())
+        return Diamond(Prop(TrueFormula()), TrueFormula(logic=Logic.LDL))
 
     @classmethod
     def ldlf_prop_false(cls, args):
-        return Diamond(Prop(FalseFormula()), LDLTrue())
+        return Diamond(Prop(FalseFormula()), TrueFormula(logic=Logic.LDL))
 
     @classmethod
     def ldlf_prop_atom(cls, args):
         assert len(args) == 1
         token = args[0]
         symbol = str(token)
-        return Diamond(Prop(Atomic(symbol)), LDLTrue())
+        return Diamond(Prop(Atomic(symbol)), TrueFormula(logic=Logic.LDL))
 
     @classmethod
     def regular_expression(cls, args):
diff --git a/pylogics/parsers/ltl.lark b/pylogics/parsers/ltl.lark
index fd9492e..d9571e0 100644
--- a/pylogics/parsers/ltl.lark
+++ b/pylogics/parsers/ltl.lark
@@ -23,28 +23,35 @@ start: ltlf_formula
 ?ltlf_weak_next:   WEAK_NEXT ltlf_unaryop
 ?ltlf_not:         NOT ltlf_unaryop
 ?ltlf_wrapped:     ltlf_atom
-             |     LSEPARATOR ltlf_formula RSEPARATOR
+             |     LEFT_PARENTHESIS ltlf_formula RIGHT_PARENTHESIS
 ?ltlf_atom:        ltlf_symbol
           |        ltlf_true
           |        ltlf_false
+          |        ltlf_tt
+          |        ltlf_ff
           |        ltlf_last
 
 ltlf_symbol: SYMBOL_NAME
 ltlf_true: prop_true
 ltlf_false: prop_false
+ltlf_tt: TT
+ltlf_ff: FF
 ltlf_last: LAST
 
 UNTIL.2: "U"
-RELEASE.2: "R"
+RELEASE.2:  /R|V/
 ALWAYS.2: "G"
 EVENTUALLY.2: "F"
-NEXT.2: "X"
-WEAK_NEXT.2: "N"
+NEXT.2: "X[!]"
+WEAK_NEXT.2: "X"
 WEAK_UNTIL.2: "W"
 STRONG_RELEASE.2: "M"
 
-END.2: /(?i:end)/
-LAST.2: /(?i:last)/
+END.2: /end/
+LAST.2: /last/
+
+TT.2: /tt/
+FF.2: /ff/
 
 %ignore /\s+/
 
@@ -56,5 +63,5 @@ LAST.2: /(?i:last)/
 %import  .pl.AND -> AND
 %import  .pl.EQUIVALENCE -> EQUIVALENCE
 %import  .pl.IMPLY -> IMPLY
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
diff --git a/pylogics/parsers/ltl.py b/pylogics/parsers/ltl.py
index 2368b4f..b09c196 100644
--- a/pylogics/parsers/ltl.py
+++ b/pylogics/parsers/ltl.py
@@ -39,6 +39,8 @@
     Eventually,
     Last,
     Next,
+    PropositionalFalse,
+    PropositionalTrue,
     Release,
     StrongRelease,
     Until,
@@ -128,10 +130,18 @@ def ltlf_atom(cls, args):
 
     @classmethod
     def ltlf_true(cls, _args):
-        return make_boolean(True, logic=Logic.LTL)
+        return PropositionalTrue()
 
     @classmethod
     def ltlf_false(cls, _args):
+        return PropositionalFalse()
+
+    @classmethod
+    def ltlf_tt(cls, _args):
+        return make_boolean(True, logic=Logic.LTL)
+
+    @classmethod
+    def ltlf_ff(cls, _args):
         return make_boolean(False, logic=Logic.LTL)
 
     @classmethod
diff --git a/pylogics/parsers/pl.lark b/pylogics/parsers/pl.lark
index 2f05db5..c5fa7f7 100644
--- a/pylogics/parsers/pl.lark
+++ b/pylogics/parsers/pl.lark
@@ -7,24 +7,24 @@ start: propositional_formula
 ?prop_and: prop_not (AND prop_not)*
 ?prop_not: NOT* prop_wrapped
 ?prop_wrapped: prop_atom
-            | LSEPARATOR propositional_formula RSEPARATOR
+            | LEFT_PARENTHESIS propositional_formula RIGHT_PARENTHESIS
 ?prop_atom: atom
-	     | prop_true
-	     | prop_false
+         | prop_true
+         | prop_false
 
 atom: SYMBOL_NAME
 prop_true: TRUE
 prop_false: FALSE
 
-LSEPARATOR : "("
-RSEPARATOR : ")"
+LEFT_PARENTHESIS : "("
+RIGHT_PARENTHESIS : ")"
 EQUIVALENCE : "<->"
 IMPLY : ">>"|"->"
 OR: "||"|"|"
 AND: "&&"|"&"
 NOT: "!"|"~"
-TRUE.2: /(?i:true)/
-FALSE.2: /(?i:false)/
+TRUE.2: /true/
+FALSE.2: /false/
 
 // Symbols cannot contain uppercase letters, because these are reserved
 // Moreover, any word between quotes is a symbol
diff --git a/pylogics/parsers/pltl.lark b/pylogics/parsers/pltl.lark
index f06a6ed..a8b1d9e 100644
--- a/pylogics/parsers/pltl.lark
+++ b/pylogics/parsers/pltl.lark
@@ -18,23 +18,30 @@ start: pltlf_formula
 ?pltlf_before:       BEFORE pltlf_unaryop
 ?pltlf_not:          NOT pltlf_unaryop
 ?pltlf_wrapped:      pltlf_atom
-             |       LSEPARATOR pltlf_formula RSEPARATOR
+             |       LEFT_PARENTHESIS pltlf_formula RIGHT_PARENTHESIS
 ?pltlf_atom:       pltlf_symbol
            |       pltlf_true
            |       pltlf_false
+           |       pltlf_tt
+           |       pltlf_ff
            |       pltlf_start
+           |       pltlf_first
 
 pltlf_symbol: SYMBOL_NAME
 pltlf_true: prop_true
 pltlf_false: prop_false
+pltlf_tt: TT
+pltlf_ff: FF
 pltlf_start: START
+pltlf_first: FIRST
 
 // Operators must not be part of a word
 SINCE.2: "S"
 HISTORICALLY.2: "H"
 ONCE.2: "O"
 BEFORE.2: "Y"
-START.2: /(?i:start)/
+FIRST.2: /first/
+START.2: /start/
 
 %ignore /\s+/
 
@@ -46,5 +53,7 @@ START.2: /(?i:start)/
 %import  .pl.AND -> AND
 %import  .pl.EQUIVALENCE -> EQUIVALENCE
 %import  .pl.IMPLY -> IMPLY
-%import  .pl.LSEPARATOR -> LSEPARATOR
-%import  .pl.RSEPARATOR -> RSEPARATOR
\ No newline at end of file
+%import  .pl.LEFT_PARENTHESIS -> LEFT_PARENTHESIS
+%import  .pl.RIGHT_PARENTHESIS -> RIGHT_PARENTHESIS
+%import  .ltl.TT -> TT
+%import  .ltl.FF -> FF
diff --git a/pylogics/parsers/pltl.py b/pylogics/parsers/pltl.py
index 349ff02..5af8d1c 100644
--- a/pylogics/parsers/pltl.py
+++ b/pylogics/parsers/pltl.py
@@ -33,7 +33,17 @@
     Or,
     make_boolean,
 )
-from pylogics.syntax.pltl import Atomic, Before, Historically, Once, Since, Start
+from pylogics.syntax.pltl import (
+    Atomic,
+    Before,
+    First,
+    Historically,
+    Once,
+    PropositionalFalse,
+    PropositionalTrue,
+    Since,
+    Start,
+)
 
 
 class _PLTLTransformer(AbstractTransformer):
@@ -99,16 +109,28 @@ def pltlf_atom(cls, args):
 
     @classmethod
     def pltlf_true(cls, _args):
-        return make_boolean(True, logic=Logic.PLTL)
+        return PropositionalTrue()
 
     @classmethod
     def pltlf_false(cls, _args):
+        return PropositionalFalse()
+
+    @classmethod
+    def pltlf_tt(cls, _args):
+        return make_boolean(True, logic=Logic.PLTL)
+
+    @classmethod
+    def pltlf_ff(cls, _args):
         return make_boolean(False, logic=Logic.PLTL)
 
     @classmethod
     def pltlf_start(cls, _args):
         return Start()
 
+    @classmethod
+    def pltlf_first(cls, _args):
+        return First()
+
     @classmethod
     def pltlf_symbol(cls, args):
         assert len(args) == 1
diff --git a/pylogics/syntax/base.py b/pylogics/syntax/base.py
index ee11298..c64dc75 100644
--- a/pylogics/syntax/base.py
+++ b/pylogics/syntax/base.py
@@ -273,10 +273,6 @@ def __hash__(self) -> int:
         """Compute the hash."""
         return hash((type(self), self.logic))
 
-    def __str__(self) -> str:
-        """Get the string representation."""
-        return "true"
-
     def __repr__(self) -> str:
         """Get an unambiguous string representation."""
         return f"TrueFormula({self.logic})"
@@ -292,7 +288,7 @@ def __eq__(self, other):
         """
         return type(other) == type(self) and self.logic == other.logic
 
-    def __neg__(self) -> Formula:
+    def __invert__(self) -> "Formula":
         """Negate."""
         return make_boolean(False, logic=self.logic)
 
@@ -333,7 +329,7 @@ def __eq__(self, other):
         """
         return type(other) == type(self) and self.logic == other.logic
 
-    def __neg__(self) -> Formula:
+    def __invert__(self) -> "Formula":
         """Negate."""
         return make_boolean(True, logic=self.logic)
 
@@ -396,10 +392,17 @@ def _simplify_monotone_op_operands(cls, *operands):
         while len(stack) > 0:
             element = stack.pop()
             if not isinstance(element, cls):
+                if ~element in seen_operands:
+                    # i.e. if you get "phi OP (INV phi)"
+                    # then we just return the absorbing element
+                    return [absorbing]
                 if element not in seen_operands:
+                    # thanks to idempotency, we can remove duplicates
                     new_operands.append(element)
                     seen_operands.add(element)
                 continue
+            # if here, we met a sub-operand of type OP
+            # we pop-up the operands of the subformula
             stack.extend(reversed(element.operands))  # see above regarding reversed.
 
         return new_operands
@@ -480,11 +483,14 @@ class Not(_UnaryOp):
 
     def __new__(cls, arg, **kwargs):
         """Instantiate the object."""
-        if isinstance(arg, Not):
-            # ~~phi = phi
-            return arg.argument
+        if isinstance(arg, (Not, TrueFormula, FalseFormula)):
+            return ~arg
         return super(Not, cls).__new__(cls)
 
+    def __invert__(self):
+        """Invert the negation."""
+        return self.argument
+
     @property
     def logic(self) -> Logic:
         """Get the logical formalism."""
diff --git a/pylogics/syntax/ldl.py b/pylogics/syntax/ldl.py
index 7a39b30..d889093 100644
--- a/pylogics/syntax/ldl.py
+++ b/pylogics/syntax/ldl.py
@@ -59,46 +59,6 @@ def logic(self) -> Logic:
         return Logic.RE
 
 
-class LDLTrue(TrueFormula):
-    """True formula of LDL."""
-
-    def __init__(self):
-        """Initialize."""
-        super().__init__(Logic.LDL)
-
-    def __str__(self) -> str:
-        """Get the string representation."""
-        return "tt"
-
-    def __repr__(self) -> str:
-        """Get an unambiguous string representation."""
-        return f"LDLTrue({self.logic})"
-
-    def __neg__(self) -> Formula:
-        """Negate."""
-        return LDLFalse()
-
-
-class LDLFalse(FalseFormula):
-    """False formula of LDL."""
-
-    def __init__(self):
-        """Initialize."""
-        super().__init__(Logic.LDL)
-
-    def __str__(self) -> str:
-        """Get the string representation."""
-        return "ff"
-
-    def __repr__(self) -> str:
-        """Get an unambiguous string representation."""
-        return f"LDLFalse({self.logic})"
-
-    def __neg__(self) -> Formula:
-        """Negate."""
-        return LDLTrue()
-
-
 class Seq(_BinaryOp, _RegularExpression):
     """Sequence of regular expressions."""
 
@@ -193,5 +153,5 @@ class Box(_TemporalFormula):
     """Box formula."""
 
 
-End = partial(Box, Prop(TrueFormula()), LDLFalse())
+End = partial(Box, Prop(TrueFormula()), FalseFormula(logic=Logic.LDL))
 Last = partial(Diamond, Prop(TrueFormula()), End())
diff --git a/pylogics/syntax/ltl.py b/pylogics/syntax/ltl.py
index ff12e19..53ed17e 100644
--- a/pylogics/syntax/ltl.py
+++ b/pylogics/syntax/ltl.py
@@ -29,6 +29,7 @@
     FalseFormula,
     Formula,
     Logic,
+    TrueFormula,
     _BinaryOp,
     _UnaryOp,
 )
@@ -63,6 +64,50 @@ class Atomic(AbstractAtomic, _LTL):
     """An atomic proposition of LTL."""
 
 
+class PropositionalTrue(TrueFormula, _LTL):
+    """
+    A propositional true.
+
+    This is equivalent to 'a | ~a'. for some atom 'a'.
+    It requires reading any symbol, at least once.
+    """
+
+    def __init__(self):
+        """Initialize."""
+        super().__init__(logic=Logic.LTL)
+
+    def __invert__(self) -> "Formula":
+        """Negate."""
+        return PropositionalFalse()
+
+    def __repr__(self) -> str:
+        """Get an unambiguous string representation."""
+        return f"PropositionalTrue({self.logic})"
+
+
+class PropositionalFalse(FalseFormula, _LTL):
+    """
+    A propositional false.
+
+    This is equivalent to 'a & ~a'. for some atom 'a'.
+    It requires reading no symbol, at least once.
+    The meaning of this formula is a bit blurred with 'ff'
+    with respect to 'tt' and 'true'.
+    """
+
+    def __init__(self):
+        """Initialize."""
+        super().__init__(logic=Logic.LTL)
+
+    def __invert__(self) -> "Formula":
+        """Negate."""
+        return PropositionalTrue()
+
+    def __repr__(self) -> str:
+        """Get an unambiguous string representation."""
+        return f"PropositionalFalse({self.logic})"
+
+
 class Next(_LTLUnaryOp):
     """The "next" formula in LTL."""
 
diff --git a/pylogics/syntax/pltl.py b/pylogics/syntax/pltl.py
index 8c3c810..82fd99a 100644
--- a/pylogics/syntax/pltl.py
+++ b/pylogics/syntax/pltl.py
@@ -29,6 +29,8 @@
     FalseFormula,
     Formula,
     Logic,
+    Not,
+    TrueFormula,
     _BinaryOp,
     _UnaryOp,
 )
@@ -63,6 +65,50 @@ class Atomic(AbstractAtomic, _PLTL):
     """An atomic proposition of PLTL."""
 
 
+class PropositionalTrue(TrueFormula, _PLTL):
+    """
+    A propositional true.
+
+    This is equivalent to 'a | ~a'. for some atom 'a'.
+    It requires reading any symbol, at least once.
+    """
+
+    def __init__(self):
+        """Initialize."""
+        super().__init__(logic=Logic.PLTL)
+
+    def __invert__(self) -> "Formula":
+        """Negate."""
+        return PropositionalFalse()
+
+    def __repr__(self) -> str:
+        """Get an unambiguous string representation."""
+        return f"PropositionalTrue({self.logic})"
+
+
+class PropositionalFalse(FalseFormula, _PLTL):
+    """
+    A propositional false.
+
+    This is equivalent to 'a & ~a'. for some atom 'a'.
+    It requires reading no symbol, at least once.
+    The meaning of this formula is a bit blurred with 'ff'
+    with respect to 'tt' and 'true'.
+    """
+
+    def __init__(self):
+        """Initialize."""
+        super().__init__(logic=Logic.PLTL)
+
+    def __invert__(self) -> "Formula":
+        """Negate."""
+        return PropositionalTrue()
+
+    def __repr__(self) -> str:
+        """Get an unambiguous string representation."""
+        return f"PropositionalFalse({self.logic})"
+
+
 class Before(_PLTLUnaryOp):
     """The "before" formula in PLTL."""
 
@@ -88,3 +134,4 @@ class Historically(_PLTLUnaryOp):
 
 
 Start = partial(Historically, FalseFormula(logic=Logic.PLTL))
+First = partial(Not, Before(TrueFormula(logic=Logic.PLTL)))
diff --git a/pylogics/utils/to_string.py b/pylogics/utils/to_string.py
index 7fbb058..9b6dcb2 100644
--- a/pylogics/utils/to_string.py
+++ b/pylogics/utils/to_string.py
@@ -32,32 +32,20 @@
     FalseFormula,
     Formula,
     Implies,
+    Logic,
     Not,
     Or,
     TrueFormula,
 )
-from pylogics.syntax.ldl import (
-    Box,
-    Diamond,
-    LDLFalse,
-    LDLTrue,
-    Prop,
-    Seq,
-    Star,
-    Test,
-    Union,
-)
-from pylogics.syntax.ltl import (
-    Always,
-    Eventually,
-    Next,
-    Release,
-    StrongRelease,
-    Until,
-    WeakNext,
-    WeakUntil,
-)
-from pylogics.syntax.pltl import Before, Historically, Once, Since
+from pylogics.syntax.ldl import Box, Diamond, Prop, Seq, Star, Test, Union
+from pylogics.syntax.ltl import Always, Eventually, Next
+from pylogics.syntax.ltl import PropositionalFalse as LTLPropositionalFalse
+from pylogics.syntax.ltl import PropositionalTrue as LTLPropositionalTrue
+from pylogics.syntax.ltl import Release, StrongRelease, Until, WeakNext, WeakUntil
+from pylogics.syntax.pltl import Before, Historically, Once
+from pylogics.syntax.pltl import PropositionalFalse as PLTLPropositionalFalse
+from pylogics.syntax.pltl import PropositionalTrue as PLTLPropositionalTrue
+from pylogics.syntax.pltl import Since
 
 
 @functools.singledispatch
@@ -108,27 +96,39 @@ def to_string_atomic(formula: AbstractAtomic) -> str:
 
 
 @to_string.register(TrueFormula)
-def to_string_true(_formula: TrueFormula) -> str:
-    """Transform the "true" formula into string."""
-    return "true"
+def to_string_logical_true(formula: TrueFormula) -> str:
+    """Transform the "tt" formula into string."""
+    return "tt" if formula.logic != Logic.PL else "true"
 
 
 @to_string.register(FalseFormula)
-def to_string_false(_formula: FalseFormula) -> str:
-    """Transform the "false" formula into string."""
+def to_string_logical_false(formula: FalseFormula) -> str:
+    """Transform the "ff" formula into string."""
+    return "ff" if formula.logic != Logic.PL else "false"
+
+
+@to_string.register
+def to_string_ltl_propositional_true(_formula: LTLPropositionalTrue) -> str:
+    """Transform the "tt" formula into string."""
+    return "true"
+
+
+@to_string.register
+def to_string_ltl_propositional_false(_formula: LTLPropositionalFalse) -> str:
+    """Transform the "ff" formula into string."""
     return "false"
 
 
 @to_string.register(Next)
 def to_string_next(formula: Next) -> str:
     """Transform a next formula into string."""
-    return f"X({to_string(formula.argument)})"
+    return f"X[!]({to_string(formula.argument)})"
 
 
 @to_string.register(WeakNext)
 def to_string_weak_next(formula: WeakNext) -> str:
     """Transform a weak next formula into string."""
-    return f"N({to_string(formula.argument)})"
+    return f"X({to_string(formula.argument)})"
 
 
 @to_string.register(Until)
@@ -167,6 +167,18 @@ def to_string_always(formula: Always) -> str:
     return f"G({to_string(formula.argument)})"
 
 
+@to_string.register
+def to_string_pltl_propositional_true(_formula: PLTLPropositionalTrue) -> str:
+    """Transform the "true" formula into string."""
+    return "true"
+
+
+@to_string.register
+def to_string_pltl_propositional_false(_formula: PLTLPropositionalFalse) -> str:
+    """Transform the "false" formula into string."""
+    return "false"
+
+
 @to_string.register(Before)
 def to_string_pltl_before(formula: Before) -> str:
     """Transform a 'before' formula into string."""
@@ -191,18 +203,6 @@ def to_string_pltl_historically(formula: Historically) -> str:
     return f"H({to_string(formula.argument)})"
 
 
-@to_string.register(LDLTrue)
-def to_string_ldl_true(_formula: LDLTrue) -> str:
-    """Transform an LDL true into string."""
-    return "tt"
-
-
-@to_string.register(LDLFalse)
-def to_string_ldl_false(_formula: LDLFalse) -> str:
-    """Transform an LDL false into string."""
-    return "ff"
-
-
 @to_string.register(Diamond)
 def to_string_ldl_diamond(formula: Diamond) -> str:
     """Transform an LDL diamond formula into string."""
diff --git a/pyproject.toml b/pyproject.toml
index eb186df..20f3f7e 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [tool.poetry]
 name = "pylogics"
-version = "0.1.0.alpha0"
+version = "0.1.0"
 description = "A Python library for logic formalisms representation and manipulation."
 authors = ["MarcoFavorito <marco.favorito@gmail.com>"]
 license = "LGPL-3.0-or-later"
@@ -69,6 +69,7 @@ markdown = "^3.3.4"
 twine = "^3.3.0"
 hypothesis = "^6.10.0"
 hypothesis-pytest = "^0.19.0"
+mistune = {version = "^2.0.0-rc.1", allow-prereleases = true}
 
 [build-system]
 requires = ["poetry>=0.12"]
diff --git a/scripts/whitelist.py b/scripts/whitelist.py
index d87b4a4..dee019d 100644
--- a/scripts/whitelist.py
+++ b/scripts/whitelist.py
@@ -24,19 +24,21 @@
 _.ldlf_prop_atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ldl.py:148)
 _.re_propositional  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ldl.py:195)
 parse_ldl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ldl.py:222)
-_.start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:51)
-_.ltlf_formula  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:56)
-_.ltlf_always  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:95)
-_.ltlf_eventually  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:99)
-_.ltlf_next  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:103)
-_.ltlf_weak_next  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:107)
-_.ltlf_not  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:111)
-_.ltlf_atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:124)
-_.ltlf_true  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:129)
-_.ltlf_false  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:133)
-_.ltlf_last  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:137)
-_.ltlf_symbol  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:141)
-parse_ltl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:159)
+_.start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:53)
+_.ltlf_formula  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:58)
+_.ltlf_always  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:97)
+_.ltlf_eventually  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:101)
+_.ltlf_next  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:105)
+_.ltlf_weak_next  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:109)
+_.ltlf_not  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:113)
+_.ltlf_atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:126)
+_.ltlf_true  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:131)
+_.ltlf_false  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:135)
+_.ltlf_tt  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:139)
+_.ltlf_ff  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:143)
+_.ltlf_last  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:147)
+_.ltlf_symbol  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:151)
+parse_ltl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/ltl.py:169)
 _.start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:42)
 _.propositional_formula  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:47)
 _.prop_not  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:70)
@@ -45,18 +47,21 @@
 _.prop_false  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:93)
 _.atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:98)
 parse_pl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pl.py:114)
-_.start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:40)
-_.pltlf_formula  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:45)
-_.pltlf_historically  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:72)
-_.pltlf_once  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:76)
-_.pltlf_before  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:80)
-_.pltlf_not  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:84)
-_.pltlf_atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:97)
-_.pltlf_true  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:102)
-_.pltlf_false  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:106)
-_.pltlf_start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:110)
-_.pltlf_symbol  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:114)
-parse_pltl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:132)
+_.start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:50)
+_.pltlf_formula  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:55)
+_.pltlf_historically  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:80)
+_.pltlf_once  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:84)
+_.pltlf_before  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:88)
+_.pltlf_not  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:92)
+_.pltlf_atom  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:105)
+_.pltlf_true  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:110)
+_.pltlf_false  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:114)
+_.pltlf_tt  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:118)
+_.pltlf_ff  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:122)
+_.pltlf_start  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:126)
+_.pltlf_first  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:130)
+_.pltlf_symbol  # unused method (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:134)
+parse_pltl  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/parsers/pltl.py:152)
 and_semantics  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/semantics/base.py:37)
 or_semantics  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/semantics/base.py:43)
 not_semantics  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/semantics/base.py:49)
@@ -73,33 +78,37 @@
 _.__context  # unused attribute (/home/marcofavorito/workfolder/pylogics/pylogics/syntax/base.py:59)
 reset_cache  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/syntax/base.py:80)
 get_cache_context  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/syntax/base.py:85)
-ensure_formula  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/syntax/base.py:577)
-to_string_and  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:74)
-to_string_or  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:80)
-to_string_not  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:86)
-to_string_implies  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:92)
-to_string_equivalence  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:98)
-to_string_atomic  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:104)
-to_string_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:110)
-to_string_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:116)
-to_string_next  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:122)
-to_string_weak_next  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:128)
-to_string_until  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:134)
-to_string_weak_until  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:140)
-to_string_release  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:146)
-to_string_strong_release  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:152)
-to_string_eventually  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:158)
-to_string_always  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:164)
-to_string_pltl_before  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:170)
-to_string_pltl_since  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:176)
-to_string_pltl_once  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:182)
-to_string_pltl_historically  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:188)
-to_string_ldl_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:194)
-to_string_ldl_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:200)
-to_string_ldl_diamond  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:206)
-to_string_ldl_box  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:212)
-to_string_re_seq  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:218)
-to_string_re_union  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:224)
-to_string_re_star  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:230)
-to_string_re_test  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:236)
-to_string_re_prop  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:242)
+ensure_formula  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/syntax/base.py:583)
+to_string_and  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:72)
+to_string_or  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:78)
+to_string_not  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:84)
+to_string_implies  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:90)
+to_string_equivalence  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:96)
+to_string_atomic  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:102)
+to_string_logical_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:108)
+to_string_logical_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:114)
+to_string_ltl_propositional_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:120)
+to_string_ltl_propositional_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:126)
+to_string_next  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:132)
+to_string_weak_next  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:138)
+to_string_until  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:144)
+to_string_weak_until  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:150)
+to_string_release  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:156)
+to_string_strong_release  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:162)
+to_string_eventually  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:168)
+to_string_always  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:174)
+to_string_pltl_propositional_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:180)
+to_string_pltl_propositional_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:186)
+to_string_pltl_before  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:192)
+to_string_pltl_since  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:198)
+to_string_pltl_once  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:204)
+to_string_pltl_historically  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:210)
+to_string_ldl_true  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:216)
+to_string_ldl_false  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:222)
+to_string_ldl_diamond  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:228)
+to_string_ldl_box  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:234)
+to_string_re_seq  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:240)
+to_string_re_union  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:246)
+to_string_re_star  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:252)
+to_string_re_test  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:258)
+to_string_re_prop  # unused function (/home/marcofavorito/workfolder/pylogics/pylogics/utils/to_string.py:264)
diff --git a/setup.cfg b/setup.cfg
index 363da90..c801ea9 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -46,6 +46,9 @@ strict_optional = True
 [mypy-lark.*]
 ignore_missing_imports = True
 
+[mypy-mistune.*]
+ignore_missing_imports = True
+
 # Per-module options for tests dir:
 
 [mypy-pytest]
diff --git a/tox.ini b/tox.ini
index 9e60942..8a4bb94 100644
--- a/tox.ini
+++ b/tox.ini
@@ -17,6 +17,7 @@ deps =
     pytest-randomly
     hypothesis-pytest
     hypothesis
+    mistune>=2.0.0-rc.1
     ; Main dependencies
     lark-parser
 


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index bc4c49b..57c1949 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -21,11 +21,21 @@
 #
 
 """The conftest.py module for pytest."""
+import inspect
+from pathlib import Path
+
 import pytest
 from hypothesis import HealthCheck, settings
 
+import pylogics
 from pylogics.syntax.base import reset_cache
 
+_current_filepah = inspect.getframeinfo(inspect.currentframe()).filename  # type: ignore
+TEST_DIRECTORY = Path(_current_filepah).absolute().parent
+ROOT_DIRECTORY = TEST_DIRECTORY.parent
+LIBRARY_DIRECTORY = ROOT_DIRECTORY / pylogics.__name__
+DOCS_DIRECTORY = ROOT_DIRECTORY / "docs"
+
 
 @pytest.fixture(scope="class", autouse=True)
 def reset_cache_fixture():
diff --git a/tests/test_base.py b/tests/test_base.py
new file mode 100644
index 0000000..de2a022
--- /dev/null
+++ b/tests/test_base.py
@@ -0,0 +1,38 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Tests on the pylogics.syntax.ltl module."""
+import pytest
+
+from pylogics.syntax.base import FalseFormula, Logic, Not, TrueFormula
+
+
+@pytest.mark.parametrize("logic", list(Logic))
+def test_not_true(logic):
+    """Test that the negation of true gives false."""
+    assert FalseFormula(logic=logic) == Not(TrueFormula(logic=logic))
+
+
+@pytest.mark.parametrize("logic", list(Logic))
+def test_not_false(logic):
+    """Test that the negation of false gives true."""
+    assert TrueFormula(logic=logic) == Not(FalseFormula(logic=logic))
diff --git a/tests/test_docs.py b/tests/test_docs.py
deleted file mode 100644
index 3ab71e8..0000000
--- a/tests/test_docs.py
+++ /dev/null
@@ -1,77 +0,0 @@
-# -*- coding: utf-8 -*-
-#
-# Copyright 2021 WhiteMech
-#
-# ------------------------------
-#
-# This file is part of pylogics.
-#
-# pylogics is free software: you can redistribute it and/or modify
-# it under the terms of the GNU Lesser General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# pylogics is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public License
-# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
-#
-
-"""Tests for the docs pages."""
-
-from pylogics.parsers import parse_ldl, parse_ltl, parse_pl, parse_pltl
-
-
-def test_pl_parsing():
-    """Test PL parsing."""
-    parse_pl("a")
-    parse_pl("b")
-    parse_pl("a & b")
-    parse_pl("a | b")
-    parse_pl("a >> b")
-    parse_pl("a <-> b")
-    parse_pl("a <-> a")
-    parse_pl("!(a)")
-    parse_pl("true & false")
-    parse_pl("true | false")
-
-
-def test_ltl_parsing():
-    """Test LTL parsing."""
-    parse_ltl("a")
-    parse_ltl("b")
-    parse_ltl("X(a)")
-    parse_ltl("N(b)")
-    parse_ltl("F(a)")
-    parse_ltl("G(b)")
-    parse_ltl("G(a -> b)")
-    parse_ltl("a U b")
-    parse_ltl("a R b")
-    parse_ltl("a W b")
-    parse_ltl("a M b")
-
-
-def test_pltl_parsing():
-    """Test PLTL parsing."""
-    parse_pltl("a")
-    parse_pltl("b")
-    parse_pltl("Y(a)")
-    parse_pltl("O(b)")
-    parse_pltl("H(a)")
-    parse_pltl("a S b")
-
-
-def test_ldl_parsing():
-    """Test LDL parsing."""
-    parse_ldl("tt")
-    parse_ldl("ff")
-    parse_ldl("<a>tt")
-    parse_ldl("[a & b]ff")
-    parse_ldl("<a + b>tt")
-    parse_ldl("<a ; b><c>tt")
-    parse_ldl("<(a ; b)*><c>tt")
-    parse_ldl("<true><a>tt")
-    parse_ldl("<(?<a>tt;true)*>(<b>tt)")
diff --git a/tests/test_docs/__init__.py b/tests/test_docs/__init__.py
new file mode 100644
index 0000000..d2472ad
--- /dev/null
+++ b/tests/test_docs/__init__.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Test documentation code."""
diff --git a/tests/test_docs/base.py b/tests/test_docs/base.py
new file mode 100644
index 0000000..c7a61e1
--- /dev/null
+++ b/tests/test_docs/base.py
@@ -0,0 +1,132 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Utilities for docs tests."""
+import traceback
+from functools import partial
+from pathlib import Path
+from typing import Dict, List, Optional
+
+import mistune
+import pytest
+
+MISTUNE_BLOCK_CODE_ID = "block_code"
+
+
+def compile_and_exec(code: str, locals_dict: Dict = None) -> Dict:
+    """
+    Compile and exec the code.
+
+    :param code: the code to execute.
+    :param locals_dict: the dictionary of local variables.
+    :return: the dictionary of locals.
+    """
+    locals_dict = {} if locals_dict is None else locals_dict
+    try:
+        code_obj = compile(code, "fakemodule", "exec")
+        exec(code_obj, locals_dict)  # nosec
+    except Exception:  # type: ignore
+        pytest.fail(
+            "The execution of the following code:\n{}\nfailed with error:\n{}".format(
+                code, traceback.format_exc()
+            )
+        )
+    return locals_dict
+
+
+class BaseTestMarkdownDocs:
+    """Base test class for testing Markdown documents."""
+
+    MD_FILE: Optional[Path] = None
+    code_blocks: List[Dict] = []
+
+    @classmethod
+    def setup_class(cls):
+        """Set up class."""
+        if cls.MD_FILE is None:
+            raise ValueError("cannot set up method as MD_FILE is None")
+        content = cls.MD_FILE.read_text()
+        markdown_parser = mistune.create_markdown(renderer=mistune.AstRenderer())
+        cls.blocks = markdown_parser(content)
+        cls.code_blocks = list(filter(cls.block_code_filter, cls.blocks))
+
+    @staticmethod
+    def block_code_filter(block: Dict) -> bool:
+        """Check Mistune block is a code block."""
+        return block["type"] == MISTUNE_BLOCK_CODE_ID
+
+    @staticmethod
+    def type_filter(type_: Optional[str], b: Dict) -> bool:
+        """
+        Check Mistune code block is of a certain type.
+
+        If the field "info" is None, return False.
+        If type_ is None, this function always return true.
+
+        :param type_: the expected type of block (optional)
+        :param b: the block dicionary.
+        :return: True if the block should be accepted, false otherwise.
+        """
+        if type_ is None:
+            return True
+        return b["info"].strip() == type_ if b["info"] is not None else False
+
+    @classmethod
+    def extract_code_blocks(cls, filter_: Optional[str] = None):
+        """Extract code blocks from .md files."""
+        actual_type_filter = partial(cls.type_filter, filter_)
+        bash_code_blocks = filter(actual_type_filter, cls.code_blocks)
+        return list(b["text"] for b in bash_code_blocks)
+
+
+class BasePythonMarkdownDocs(BaseTestMarkdownDocs):
+    """Test Markdown documentation by running Python snippets in sequence."""
+
+    @classmethod
+    def setup_class(cls):
+        """
+        Set up class.
+
+        It sets the initial value of locals and globals.
+        """
+        super().setup_class()
+        cls.locals = {}
+        cls.globals = {}
+
+    @classmethod
+    def _python_selector(cls, block: Dict) -> bool:
+        return block["type"] == MISTUNE_BLOCK_CODE_ID and (
+            block["info"].strip() == "python" if block["info"] else False
+        )
+
+    def _assert(self, locals_, *mocks):
+        """Do assertions after Python code execution."""
+
+    def test_python_blocks(self, *mocks):
+        """Run Python code block in sequence."""
+        python_blocks = list(filter(self._python_selector, self.blocks))
+
+        globals_, locals_ = self.globals, self.locals
+        for python_block in python_blocks:
+            python_code = python_block["text"]
+            exec(python_code, globals_, locals_)  # nosec
+        self._assert(locals_, *mocks)
diff --git a/tests/test_docs/test_grammars.py b/tests/test_docs/test_grammars.py
new file mode 100644
index 0000000..354eed0
--- /dev/null
+++ b/tests/test_docs/test_grammars.py
@@ -0,0 +1,61 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Test grammars documentation page."""
+
+from tests.conftest import DOCS_DIRECTORY, LIBRARY_DIRECTORY
+from tests.test_docs.base import BaseTestMarkdownDocs
+
+
+class TestGrammars(BaseTestMarkdownDocs):
+    """Test that the docs' and library's grammars are the same."""
+
+    MD_FILE = DOCS_DIRECTORY / "grammars.md"
+
+    @classmethod
+    def setup_class(cls):
+        """Set up the test."""
+        super().setup_class()
+
+        grammar_directory = LIBRARY_DIRECTORY / "parsers"
+
+        for formalism in ["pl", "ltl", "pltl", "ldl"]:
+            text = (grammar_directory / f"{formalism}.lark").read_text()
+            setattr(cls, formalism + "_grammar", text)
+
+        cls.actual_grammars = cls.extract_code_blocks(filter_="lark")
+
+    def test_pl_grammar(self):
+        """Test PL grammar is as expected."""
+        assert self.actual_grammars[0] == self.pl_grammar
+
+    def test_ltl_grammar(self):
+        """Test LTL grammar is as expected."""
+        assert self.actual_grammars[1] == self.ltl_grammar
+
+    def test_pltl_grammar(self):
+        """Test PLTL grammar is as expected."""
+        assert self.actual_grammars[2] == self.pltl_grammar
+
+    def test_ldl_grammar(self):
+        """Test LDL grammar is as expected."""
+        assert self.actual_grammars[3] == self.ldl_grammar
diff --git a/tests/test_docs/test_introduction.py b/tests/test_docs/test_introduction.py
new file mode 100644
index 0000000..cab80ba
--- /dev/null
+++ b/tests/test_docs/test_introduction.py
@@ -0,0 +1,36 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Test introduction documentation page."""
+from pylogics.semantics.pl import evaluate_pl
+from tests.conftest import DOCS_DIRECTORY
+from tests.test_docs.base import BasePythonMarkdownDocs
+
+
+class TestIntroduction(BasePythonMarkdownDocs):
+    """Test that the code snippet in the introduction are correct."""
+
+    MD_FILE = DOCS_DIRECTORY / "introduction.md"
+
+    def _assert(self, locals_, *mocks):
+        """Do assertions after Python code execution."""
+        assert self.locals["evaluate_pl"] == evaluate_pl
diff --git a/tests/test_docs/test_parsing.py b/tests/test_docs/test_parsing.py
new file mode 100644
index 0000000..5a30f5d
--- /dev/null
+++ b/tests/test_docs/test_parsing.py
@@ -0,0 +1,31 @@
+# -*- coding: utf-8 -*-
+#
+# Copyright 2021 WhiteMech
+#
+# ------------------------------
+#
+# This file is part of pylogics.
+#
+# pylogics is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# pylogics is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with pylogics.  If not, see <https://www.gnu.org/licenses/>.
+#
+
+"""Test parsing documentation page."""
+from tests.conftest import DOCS_DIRECTORY
+from tests.test_docs.base import BasePythonMarkdownDocs
+
+
+class TestParsing(BasePythonMarkdownDocs):
+    """Test that the code snippet in the parsing page run without failure."""
+
+    MD_FILE = DOCS_DIRECTORY / "parsing.md"