diff --git a/src/liblet/grammar.py b/src/liblet/grammar.py
index 275601e..b54f9f4 100644
--- a/src/liblet/grammar.py
+++ b/src/liblet/grammar.py
@@ -45,11 +45,11 @@ class Production:
def __init__(self, lhs, rhs):
if isinstance(lhs, str) and lhs:
self.lhs = lhs
- elif isinstance(lhs, (list, tuple)) and all(map(lambda _: isinstance(_, str) and _, lhs)):
+ elif isinstance(lhs, list | tuple) and all(isinstance(_, str) and _ for _ in lhs):
self.lhs = tuple(lhs)
else:
raise ValueError('The left-hand side is not a nonempty str, nor a tuple (or list) of nonempty str.')
- if isinstance(rhs, (list, tuple)) and rhs and all(map(lambda _: isinstance(_, str) and _, rhs)):
+ if isinstance(rhs, list | tuple) and rhs and all(isinstance(_, str) and _ for _ in rhs):
self.rhs = tuple(rhs)
else:
raise ValueError('The right-hand side is not a tuple (or list) of nonempty str.')
@@ -78,7 +78,7 @@ def __repr__(self):
@classmethod
def from_string(cls, prods, context_free=True): # pragma: no cover
"""Deprecated. Use Productions.from_string"""
- wwarn('The function "from_string" has been moved to Productions.', DeprecationWarning)
+ wwarn('The function "from_string" has been moved to Productions.', DeprecationWarning, stacklevel=2)
return Productions.from_string(prods, context_free)
@classmethod
@@ -114,7 +114,7 @@ def such_that(cls, **kwargs):
[A -> B C]
>>> list(filter(Production.such_that(lhs='B', rhs_len=1), prods))
[B -> b]
- """
+ """ # noqa: RUF002
conditions = []
if 'lhs' in kwargs:
conditions.append(lambda P: P.lhs == kwargs['lhs'] if isinstance(kwargs['lhs'], str) else tuple(kwargs['lhs']))
@@ -138,6 +138,8 @@ class Productions(tuple):
The main purpose of this class is to allow a nicer HTML representation of the grammar productions.
"""
+ __slots__ = ()
+
@classmethod
def from_string(cls, prods, context_free=True):
"""Builds a tuple of *productions* obtained from the given string.
@@ -173,27 +175,26 @@ def from_string(cls, prods, context_free=True):
f'Production "{p}" has more than one symbol as left-hand side, that is forbidden in a context-free grammar.'
)
lhs = lhs[0]
- for rh in rha.split('|'):
- P.append(Production(lhs, tuple(rh.split())))
+ P.extend(Production(lhs, tuple(rh.split())) for rh in rha.split('|'))
return cls(P)
def _repr_html_(self): # pragma: no cover
rows = []
klhs = lambda _: _[1].lhs
for lhs, rhss in groupby(sorted(enumerate(self), key=klhs), klhs):
- rhss = list(rhss) # it's used by min and map in format
+ rhssl = list(rhss) # it's used by min and map in format
rows.append(
(
- min(map(lambda _: _[0], rhss)),
+ min(_[0] for _ in rhssl),
'
{} | {} '.format(
_letlrhstostr(lhs),
- ' | '.join(map(lambda _: f'{_letlrhstostr(_[1].rhs)}({_[0]})', rhss)),
+ ' | '.join(f'{_letlrhstostr(_[1].rhs)}({_[0]})' for _ in rhssl),
),
)
)
return (
''
- + ' '.join(map(lambda _: _[1], sorted(rows)))
+ + ' '.join(_[1] for _ in sorted(rows))
+ ' '
)
@@ -296,7 +297,7 @@ def __init__(self, N, T, P, S):
self.T = frozenset(T)
self.P = Productions(P)
self.S = S
- self.is_context_free = all(map(lambda _: isinstance(_.lhs, str), self.P))
+ self.is_context_free = all(isinstance(_.lhs, str) for _ in self.P)
if self.N & self.T:
raise ValueError(
f'The set of terminals and nonterminals are not disjoint, but have {set(self.N & self.T)} in common.'
@@ -357,9 +358,8 @@ def from_string(cls, prods, context_free=True):
N = {_ for _ in symbols if _[0].isupper()}
T = symbols - N - {ε}
G = cls(N, T, P, S)
- if context_free: # pragma: no cover
- if not G.is_context_free:
- raise ValueError('The resulting grammar is not context-free, even if so requested.')
+ if context_free and not G.is_context_free: # pragma: no cover
+ raise ValueError('The resulting grammar is not context-free, even if so requested.')
return G
def alternatives(self, N):
@@ -408,7 +408,7 @@ def __init__(self, G, start=None):
if start not in G.N:
raise ValueError('The start symbol must be a nonterminal')
self.start = start
- self._steps = tuple()
+ self._steps = ()
# the following attrs are computed
self._sf = (self.start,)
self._repr = self.start
@@ -433,8 +433,7 @@ def __ensure_prod_idx__(self, prod): # pragma: no cover
if prod in self.G.P:
return self.G.P.index(prod)
raise ValueError(f'Production {prod} does not belong to G')
- else:
- raise ValueError('The argument is not a production or an integer')
+ raise TypeError('The argument is not a production or an integer')
def leftmost(self, prod):
"""Performs a *leftmost* derivation step.
@@ -457,14 +456,12 @@ def _leftmost(derivation, prod):
if symbol in derivation.G.N:
if derivation.G.P[prod].lhs == symbol:
return derivation.step(prod, pos)
- else:
- raise ValueError(
- f'Cannot apply {derivation.G.P[prod]}: the leftmost nonterminal of {HAIR_SPACE.join(derivation._sf)} is {symbol}.'
- )
- else:
- raise ValueError(
- f'Cannot apply {derivation.G.P[prod]}: there are no nonterminals in {HAIR_SPACE.join(derivation._sf,)}.'
- )
+ raise ValueError(
+ f'Cannot apply {derivation.G.P[prod]}: the leftmost nonterminal of {HAIR_SPACE.join(derivation._sf)} is {symbol}.'
+ )
+ raise ValueError(
+ f'Cannot apply {derivation.G.P[prod]}: there are no nonterminals in {HAIR_SPACE.join(derivation._sf,)}.'
+ )
if not self.G.is_context_free:
raise ValueError('Cannot perform a leftmost derivation on a non context-free grammar')
@@ -497,14 +494,12 @@ def _rightmost(derivation, prod):
if symbol in derivation.G.N:
if derivation.G.P[prod].lhs == symbol:
return derivation.step(prod, pos)
- else:
- raise ValueError(
- f'Cannot apply {derivation.G.P[prod]}: the rightmost nonterminal of {HAIR_SPACE.join(derivation._sf)} is {symbol}.'
- )
- else:
- raise ValueError(
- f'Cannot apply {derivation.G.P[prod]}: there are no nonterminals in {HAIR_SPACE.join(derivation._sf,)}.'
- )
+ raise ValueError(
+ f'Cannot apply {derivation.G.P[prod]}: the rightmost nonterminal of {HAIR_SPACE.join(derivation._sf)} is {symbol}.'
+ )
+ raise ValueError(
+ f'Cannot apply {derivation.G.P[prod]}: there are no nonterminals in {HAIR_SPACE.join(derivation._sf,)}.'
+ )
if not self.G.is_context_free:
raise ValueError('Cannot perform a rightmost derivation on a non context-free grammar')
@@ -540,7 +535,7 @@ def _step(derivation, prod, pos):
raise ValueError(f'Cannot apply {P} at position {pos} of {HAIR_SPACE.join(sf)}.')
copy = Derivation(derivation.G, self.start)
copy._sf = tuple(_ for _ in sf[:pos] + P.rhs + sf[pos + len(P.lhs) :] if _ != ε)
- copy._steps = derivation._steps + ((prod, pos),)
+ copy._steps = (*derivation._steps, (prod, pos))
copy._repr = derivation._repr + ' -> ' + HAIR_SPACE.join(copy._sf)
return copy
@@ -567,7 +562,7 @@ def possible_steps(self, prod=None, pos=None):
Yields:
Pairs of ``(pord, pos)`` that can be used as :func:`step` argument.
"""
- type0_prods = tuple(map(lambda _: _.as_type0(), self.G.P))
+ type0_prods = tuple(_.as_type0() for _ in self.G.P)
for n, P in enumerate(type0_prods) if prod is None else ((prod, type0_prods[prod]),):
for p in range(len(self._sf) - len(P.lhs) + 1) if pos is None else (pos,):
if self._sf[p : p + len(P.lhs)] == P.lhs:
|