!/usr/bin/env python import copy import re import sys import logging
# read in a tutorial, and check the structure of it. file = sys.argv if len(sys.argv) > 2 and sys.argv == ‘–debug’:
logging.basicConfig(level=logging.DEBUG)
else:
logging.basicConfig(level=logging.ERROR)
tuto = open(file, ‘r’)
boxes = r’^(*>[s>]*)‘ box_open = r’<(*)-title>(.*)</*-title>‘ box_close = r’{:s*(.*)s*}‘ whitespace = r’^(s*)‘ pre = r’^“‘’ in_pre = False
tag_stack = [] prev_depth = 0
def stripN(line, count):
c = copy.copy(line)
for i in range(count):
c = c.lstrip()
c = c.lstrip('>')
c = c.lstrip()
return c
def skippable_tag(tags, check):
for tag in tags:
if tag in check:
return True
return False
BASE_SKIPPABLE_TAGS = [‘hidden’, ‘quote’, ‘spoken’, ‘code-2col’] BASE_EXPECTED_TAGS = [
'hands_on', 'tip', 'details'
]
for line, text in enumerate(tuto.read().split(‘n’)):
m = re.match(boxes, text)
if m:
depth = m.group(1).count('>')
else:
depth = 0
logging.debug(f'A|{"[pre]" if in_pre else ""} depth={depth} line={line} {text:120s} tss={[x["tag"] for x in tag_stack]}')
mw = re.match(whitespace, text).group(1)
# Handling pre toggling
unprefixed = stripN(text, depth)
pre_toggle = re.match(pre, unprefixed)
if not in_pre and depth > prev_depth:
unprefixed = stripN(text, depth)
m1 = re.match(box_open, unprefixed)
if m1:
tag = m1.group(1).replace('-', '_')
logging.debug(f'B|{"[pre]" if in_pre else ""} line={line} {mw}{" " * (depth - 1)}<{tag}>')
tag_stack.append({
'tag': tag,
'line': line,
'text': text,
'mw': mw,
})
else:
logging.debug(f'C|{"[pre]" if in_pre else ""} LINE={line} {mw}{" " * (depth - 1)}<NONE>')
tag_stack.append({
'tag': None,
'line': line,
'text': text,
'mw': mw,
})
logging.debug(f"D|{'[pre]' if in_pre else ''} {line} Potential Quote/Hidden/Spoken")
# error?
elif not in_pre and depth < prev_depth:
unprefixed = stripN(text, depth)
m1 = re.match(box_close, unprefixed.strip())
logging.debug(f'E|{"[pre]" if in_pre else ""} prev={prev_depth} -> curr={depth} line={line} m1={m1} ({box_close} =~ {unprefixed}) ts={len(tag_stack)} tss={[x["tag"] for x in tag_stack]}')
if m1 is None:
message = f"Potential broken box. A {tag_stack[-1]['tag']} was opened on {tag_stack[-1]['line']}, but not closed on line {line}"
print(f"{file}:{line}: {message}")
logging.warning(f"{file}:{line}: {message}")
logging.debug(f'F|{"[pre]" if in_pre else ""} NONE {line} |{text}|')
logging.debug(tag_stack[-1])
logging.debug(f"{mw}{' ' * (depth)}</NONE>")
tag_stack.pop()
else:
if any([skippable_tag(BASE_SKIPPABLE_TAGS, t) for t in m1.groups()]):
logging.debug(f"G|{'[pre]' if in_pre else ''} {mw}{' ' * (depth)}</NONE-skip tag={m1.groups()[0]}>")
logging.debug(f'H|NONE {line} |{text}|')
if ('code-2col' in m1.groups()[0] or 'hidden' in m1.groups()[0]) and (len(tag_stack) == 0 or tag_stack[-1]['tag'] is not None):
pass
# This is a special case.
# Here we don't have tag_stack[-1]['tag'] is None
# Because there wasn't even a blank header line before the 2col started.
else:
tag_stack.pop()
else:
closing_tags = m1.groups(1)[0].replace('-', '_').lstrip('.').split('.')
closing_tag = closing_tags[0].strip()
logging.debug(tag_stack[-1])
logging.debug(f"I|{mw}{' ' * (depth)}</{closing_tag}>")
if len(tag_stack) == 0:
message = f"Potential broken was closed with {closing_tag} on line {line}"
print(f"{file}:{line}: {message}")
logging.warning(f"{file}:{line}: {message}")
if tag_stack[-1]['tag'] == closing_tag:
p = tag_stack.pop()
else:
logging.debug(f'J|{"[pre]" if in_pre else ""} prev={prev_depth} -> curr={depth} line={line} m={m1} c={closing_tags}')
if not (tag_stack[-1]['tag'] is None and closing_tag not in BASE_EXPECTED_TAGS):
message = f"A {tag_stack[-1]['tag']} was opened, but closed with {closing_tag} on line {line}"
print(f"{file}:{line}: {message}")
logging.warning(f"{file}:{line}: {message}")
else:
p = tag_stack.pop()
else:
pass
#unprefixed = stripN(text, depth)
#pre_toggle = re.match(pre, unprefixed)
#if pre_toggle:
# in_pre = not in_pre
# logging.debug(f'{"[pre]" if in_pre else ""} line={line} PRE {"OPEN" if in_pre else "CLOSE"} {text}')
if pre_toggle:
in_pre = not in_pre
prev_depth = depth