refactor: make all operators generators

Except `format.counter`, of course.
This commit is contained in:
Johann Dreo 2026-04-06 16:10:05 +02:00
commit 1418055706

View file

@ -49,21 +49,22 @@ class consume:
class lines(Consume):
"""Consume line by line."""
def __call__(self, stream):
return stream.readlines()
for line in stream:
yield line
class paragraphs(Consume):
"""Consume paragraph by paragraph (separated by an empty line)."""
def __call__(self, stream):
pars = []
current = ""
for item in stream.readlines():
for item in stream:
# Not counting spaces as legit content.
if item.strip():
current += item
else:
pars.append( current )
yield current
current = ""
return pars
if current.strip():
yield current
class sections(Consume):
"""Consume section by section. A new section starts when a line matches the `mark` regexp. If `skip` is set to 'skip', the marked line is not consumed."""
@ -77,19 +78,17 @@ class consume:
self.skip = bool(skip)
def __call__(self, stream):
sec = []
current = ""
for item in stream.readlines():
for item in stream:
if re.match(self.mark, item[0]):
sec.append( current )
yield current
if self.skip:
current = ""
else:
current = item
else:
current += item
sec.append( current )
return sec
yield current
class nlines(Consume):
"""Consume by groups of `nb` lines."""
@ -97,19 +96,17 @@ class consume:
self.nb = int(nb)
def __call__(self, stream):
sec = []
count = 0
current = ""
for item in stream.readlines():
for item in stream:
if count >= self.nb:
sec.append( current )
yield current
current = ""
count = 0
else:
current += item
count += 1
sec.append( current )
return sec
yield current
class format:
@ -120,7 +117,8 @@ class format:
class asis(Format):
"""Do not format anything."""
def __call__(self, items):
return items
for item in items:
yield item
class trim(Format):
"""Split items if their length is longer than `max`, and create new items with the remaining parts."""
@ -137,57 +135,47 @@ class format:
yield item
def __call__(self, items):
trimmed = []
for item in items:
for separated in self.trim(item):
trimmed.append(separated)
return trimmed
yield separated
class eol(Format):
"""Add an end of line after the item."""
def __call__(self, items):
eoled = []
for item in items:
eoled.append( item + "\n" )
return eoled
yield item + "\n"
class strip(Format):
"""Remove any space character around the item."""
def __call__(self, items):
stripped = []
for item in items:
stripped.append( item.strip() )
return stripped
yield item.strip()
class skip(Format):
"""Skip items containing only spaces or being empty."""
def __call__(self, items):
res = []
for item in items:
if item.strip():
res.append( item )
return res
yield item
class glue(Format):
"""Glue consecutive items together if they are not separated by an empty one."""
def __call__(self, items):
glued = []
current = ""
for item in items:
if item.strip():
current += item
else:
glued.append( current )
yield current
current = ""
return glued
if current.strip():
yield current
class panel(Format):
"""Surround each item by an ascii-art box. NOTE: only works when lifted on stdout."""
def __call__(self, items):
panel = []
for item in items:
panel.append( Panel.fit(item.strip()) )
return panel
yield Panel.fit(item.strip())
class counter(Format):
"""Add a counter at the end of each items, with the current index and the total. If `end` is given, it is added at the very last item. If `sep` is given, it is appended to the item before the counter itself."""
@ -195,14 +183,16 @@ class format:
self.sep = sep
self.end = end
def __call__(self, items):
def __call__(self, counted):
items = list(counted) # Consume everything at once.
total = len(items)
res = []
for i,item in enumerate(items):
res.append(f"{item}{self.sep}{i+1}/{total}")
if res:
res[-1] += self.end
return res
for r in res:
yield r
class suffix(Format):
"""Add the `content` string after each item. If `sep` is given, it is appended to the item before the content and after the item."""
@ -211,7 +201,8 @@ class format:
self.sep = sep
def __call__(self, items):
return [i+self.sep+self.content for i in items]
for i in items:
yield i+self.sep+self.content
class prefix(Format):
"""Add the `content` string before each item. If `sep` is given, it is prepended to the item after the content and before the item."""
@ -220,18 +211,26 @@ class format:
self.sep = sep
def __call__(self, items):
return [self.content+self.sep+i for i in items]
for i in items:
yield self.content+self.sep+i
class lift:
class Lift:
def __call__(self, items):
def call(self, items):
raise NotImplementedError
def __call__(self, items):
count = 0
for item in items:
self.call(item)
count += 1
logger.debug(f"│ │ ├ {count}th item")
logger.debug(f"│ │ └OK {count} items")
class stdout(Lift):
"""Print the items on the standard output."""
def __call__(self, items):
for item in items:
def call(self, item):
if item:
if __debug__:
print(item, end = '', file = sys.stdout, flush = True)
@ -273,27 +272,24 @@ class Forthlifter:
logger.debug(f"│ │ ├ {type(self.consumer).__name__}({type(streamer).__name__})")
# Concatenate
items += self.consumer(streamer())
logger.debug(f"│ │ ├ {len(items)} items")
logger.debug(f"│ │ └OK {len(items)} items")
logger.debug(f"│ │ └OK")
return items
def format(self, items):
logger.debug(f"│ ├ format {len(items)} items")
logger.debug(f"│ ├ format")
for formatter in self.formatters:
logger.debug(f"│ │ ├ {type(formatter).__name__}")
# Replace
items = formatter(items)
logger.debug(f"│ │ ├ {len(items)} items")
logger.debug(f"│ │ └OK {len(items)} items")
logger.debug(f"│ │ └OK")
return items
def lift(self, items):
logger.debug(f"│ ├ lift {len(items)} items")
logger.debug(f"│ ├ lift")
for lifter in self.lifters:
logger.debug(f"│ │ ├ {type(lifter).__name__}")
# Call
lifter(items)
logger.debug(f"│ │ └OK {len(items)} items")
def __call__(self):
logger.debug("├ call")