!/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