Sat, 25 May 2019 09:41:33 +0200
added work on header check
import re import linetypes class Header: def __init__(self): self.description = None self.name = None self.author = None self.username = None self.filetype = None self.qualifiers = None self.license = None self.help = [] self.bfc = None self.category = None self.keywords = [] self.cmdline = None self.history = [] class BadHeader: def __init__(self, index, reason): self.index = index self.reason = reason def __repr__(self): return str.format( 'header.BadHeader(index = {index!r}, reason = {reason!r})', index = self.index, reason = self.reason, ) geometrical_types = [ linetypes.LineSegment, linetypes.Triangle, linetypes.Quadrilateral, linetypes.ConditionalLine, ] def is_suitable_header_object(entry): return not any( isinstance(entry, linetype) for linetype in [ *geometrical_types, linetypes.Comment, linetypes.Error, ] ) class HeaderError(Exception): def __init__(self, index, reason): self.index, self.reason = index, reason def __repr__(self): return str.format( 'HeaderError({index!r}, {reason!r})', index = self.index, reason = self.reason, ) def __str__(self): return reason class HeaderParser: def __init__(self): self.model_body = None self.cursor = 0 self.problems = [] def parse(self, model_body): result = Header() self.order = [] self.cursor = -1 self.model_body = model_body self.skip_to_next() result.description = self.current() self.skip_to_next() result.name = self.parse_pattern('^Name: (.+)$', 'name')[0] self.skip_to_next() result.author = self.parse_pattern('^Author: (.+)$', 'author')[0] for header_entry in self.get_more_header_stuff(): if self.try_to_match( '^!LDRAW_ORG ' + '(' \ '(?:Unofficial_)?' \ 'Part|' \ 'Subpart|' \ 'Primitive|' \ '8_Primitive|' \ '48_Primitive|' \ 'Shortcut' \ ')\s?' \ '(ORIGINAL|UPDATE \d\d\d\d-\d\d)?$', 'part type'): result.filetype, result.qualifiers = self.groups elif self.try_to_match( '^!LICENSE (.+)$', 'license'): result.license = self.groups() else: self.parse_error("couldn't handle header metacommand: " + repr(header_entry.text)) return { 'header': result, 'end-index': self.cursor + 1, } def parse_error(self, message): raise HeaderError(index = self.cursor, reason = message) def get_more_header_stuff(self): while True: self.cursor += 1 if self.cursor >= len(self.model_body): break entry = self.model_body[self.cursor] if not is_suitable_header_object(entry): break if isinstance(entry, linetypes.MetaCommand): yield entry def skip_to_next(self, *, spaces_expected = 0): while True: if self.cursor + 1 >= len(self.model_body): self.parse_error('stub ldraw file') self.cursor += 1 entry = self.model_body[self.cursor] if not is_suitable_header_object(entry): self.parse_error('header is incomplete') if isinstance(entry, linetypes.MetaCommand): return def try_to_match(self, pattern, patterntype): try: self.groups = self.parse_pattern(pattern, patterntype) except: return False def current(self): entry = self.model_body[self.cursor] assert isinstance(entry, linetypes.MetaCommand) return entry.text def parse_pattern(self, pattern, description): match = re.search(pattern, self.current()) if match: self.order.append(description) return match.groups() else: self.parse_error(str.format("couldn't parse {}", description))