# -*- coding: utf-8 -*-
# This file is part of the parce Python package.
# Copyright © 2019-2020 by Wilbert Berendsen <firstname.lastname@example.org>
# This module is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# This module is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
A Target describes where to go.
In a lexicon rule, you can specify destinations using integers or lexicons.
A Target generalizes this in two values/attributes: ``pop`` and ``push``.
``pop`` is zero or a negative integer, determining how many lexicons to
pop off the current state/context.
``push`` is a list of zero or more lexicons, determining which lexicons to
add to the current state.
You can sort of "add" targets using a TargetFactory, which can create single
Target objects combining multiple targets in once.
#: Used by the :mod:`.lexer` to describe lexicon changes.
Target = collections.namedtuple("Target", "pop push")
Target.pop.__doc__ = "A negative integer or 0, describing how many lexicons to leave."
Target.push.__doc__ = "A list of zero or more Lexicons to enter."
"""Maintains a current target and allows you to store changes.
Call get() to get the final Target, and to reset the factory's internal
__slots__ = '_pop', '_push'
self._pop = 0
self._push = 
[docs] def add(self, target):
"""Add a Target to this factory."""
if target.pop == 0:
elif -target.pop <= len(self._push):
self._push[target.pop:] = target.push
self._pop += len(self._push) + target.pop
self._push[:] = target.push
[docs] def get(self):
"""Return the current :class:`Target`.
Returns None if there is nothing to pop and push.
After this the current target is reset.
if self._pop or self._push:
t = Target(self._pop, self._push)
[docs] def push(self, *lexicons):
"""Enter one or more lexicon(s)."""
[docs] def pop(self, pop=-1):
"""Pop off one (or more) lexicon(s)."""
if -pop <= len(self._push):
self._pop += len(self._push) + pop
def make(cls, lexicon, rule):
"""Create a Target of a rule."""
f = cls()
for t in rule:
if isinstance(t, int):
if t < 0: