Files
klammertext/sks/table/span.py

112 lines
3.4 KiB
Python
Raw Normal View History

import sys
import re
syntax_description = """
A "span" is an integer (the "index") followed by an optional description of one
or more sequence subsets. A subset is defined by a series of subset
descriptions, separated by a comma. A subset description is either an integer,
two integers separated by a hypen to indicate a range, or an integer followed
only by a hyphen, which will include all the following elements of the span to
the end. No spaces are allowed in a span. All indices are zero-origin.
Span examples for an index of "3":
3
3(1)
3(0-4)
3(1-4,6-9)
3(5-)
Note that some sequence subsets must include two numbers, for example, border
lines in a table.
""".strip()
class Span:
def __init__(self, count, spec):
def parse_range(match):
start, hyphen, end = match.groups()
if hyphen is None and end is None:
end = start
elif end is None:
end = count - 1
return [int(start), int(end)]
self.all = True
subset_pat = "[-\\d,]+"
span_rgx = re.compile(f"(\d+)(\({subset_pat}\))*")
match = span_rgx.match(spec)
self.spec = spec
if match and match.group(0) == spec:
range_rgx = re.compile("(\d+)(-)?(\d+)?")
self.index = int(match.group(1))
if (match.group(2)):
self.subsets = match.group(2).strip("()").split(",")
matches = [range_rgx.match(e) for e in self.subsets]
self.ranges = [parse_range(e) if e else None for e in matches]
self.all = False
else:
self.ranges = [[0, count-1]]
else:
print(f'The span specification "{spec}" is incorrect.\n\n{syntax_description}\n')
sys.exit(1)
def __str__(self):
subsets = "all" if self.all else ",".join([f"{e[0]}-{e[1]}" for e in self.ranges])
return f"{self.index}[{subsets}]"
def __repr__(self):
return self.__str__()
class Spanset:
def __init__(self, count, span_specs):
self.count = count
self.spans = {}
for specs in span_specs.split():
for spec in self.parse_spec(specs):
print("Spanset spec:", spec)
self.spans[spec.index] = spec
def parse_spec(self, spec):
named_spec = {"top" : ["0"],
"bottom" : [str(self.count-1)],
"head" : ["1"],
"outer" : ["0", str(self.count-1)],
"inner" : [str(e) for e in range(1,self.count-1)],
"all" : [str(e) for e in range(0,self.count+1)]
}.get(spec)
if named_spec is None:
return [Span(self.count, spec)]
else:
return [Span(self.count, e) for e in named_spec]
if __name__ == "__main__":
import pprint
for s in [
Span(10, "0"),
Span(10, "1"),
Span(10, "2(2)"),
Span(10, "3(2-)"),
Span(10, "4(2-4)"),
Span(10, "5(2-4)"),
Span(10, "6(2-4,6)"),
Span(10, "7(2,6-8)"),
Span(10, "8(2,7-8,9-11,13-14)")]:
print(s.spec, "->", s)
print("Spanset")
S = Spanset(10, "1")
for name in "top bottom head outer inner all 1(2-3)".split():
print(name, "->", S.parse_spec(name))
print("Instantiate:")
s = Spanset(10, "3(1-2,4-5)")
print(s.tex_hline(3))