docs: def p_formula(self, p):
          """formula : formula EQUIVALENCE formula
                     | formula IMPLIES formula
                     | formula OR formula
                     | formula AND formula
                     | formula UNTIL formula
                     | formula RELEASE formula
                     | EVENTUALLY formula
                     | ALWAYS formula
                     | NEXT formula
                     | WEAK_NEXT formula
                     | NOT formula
                     | TRUE
                     | FALSE
                     | ATOM"""
          if len(p) == 2:
              if p[1] == Symbols.TRUE.value:
                  p[0] = LTLfTrue()
              elif p[1] == Symbols.FALSE.value:
                  p[0] = LTLfFalse()
              else:
                  p[0] = LTLfAtomic(Symbol(p[1]))
          elif len(p) == 3:
              if p[1] == Symbols.NEXT.value:
                  p[0] = LTLfNext(p[2])
              elif p[1] == Symbols.WEAK_NEXT.value:
                  p[0] = LTLfWeakNext(p[2])
              elif p[1] == Symbols.EVENTUALLY.value:
                  p[0] = LTLfEventually(p[2])
              elif p[1] == Symbols.ALWAYS.value:
                  p[0] = LTLfAlways(p[2])
              elif p[1] == Symbols.NOT.value:
                  p[0] = LTLfNot(p[2])
          elif len(p) == 4:
              l, o, r = p[1:]
              if o == Symbols.EQUIVALENCE.value:
                  p[0] = LTLfEquivalence([l, r])
              elif o == Symbols.IMPLIES.value:
                  p[0] = LTLfImplies([l, r])
              elif o == Symbols.OR.value:
                  p[0] = LTLfOr([l, r])
              elif o == Symbols.AND.value:
                  p[0] = LTLfAnd([l, r])
              elif o == Symbols.UNTIL.value:
                  p[0] = LTLfUntil([l, r])
              elif o == Symbols.RELEASE.value:
                  p[0] = LTLfRelease([l, r])
              else:
                  raise ValueError
          else:
              raise ValueError