header.py

changeset 47
4da025d0b283
child 48
38b0919c1934
equal deleted inserted replaced
39:abc83875167e 47:4da025d0b283
1 import re
2 import linetypes
3
4 class Header:
5 def __init__(self):
6 self.description = None
7 self.name = None
8 self.author = None
9 self.username = None
10 self.filetype = None
11 self.qualifiers = None
12 self.license = None
13 self.help = []
14 self.bfc = None
15 self.category = None
16 self.keywords = []
17 self.cmdline = None
18 self.history = []
19
20 class BadHeader:
21 def __init__(self, index, reason):
22 self.index = index
23 self.reason = reason
24 def __repr__(self):
25 return str.format(
26 'header.BadHeader(index = {index!r}, reason = {reason!r})',
27 index = self.index,
28 reason = self.reason,
29 )
30
31 geometrical_types = [
32 linetypes.LineSegment,
33 linetypes.Triangle,
34 linetypes.Quadrilateral,
35 linetypes.ConditionalLine,
36 ]
37
38 def is_suitable_header_object(entry):
39 return not any(
40 isinstance(entry, linetype)
41 for linetype in [
42 *geometrical_types,
43 linetypes.Comment,
44 linetypes.Error,
45 ]
46 )
47
48 class HeaderError(Exception):
49 def __init__(self, index, reason):
50 self.index, self.reason = index, reason
51 def __repr__(self):
52 return str.format(
53 'HeaderError({index!r}, {reason!r})',
54 index = self.index,
55 reason = self.reason,
56 )
57 def __str__(self):
58 return reason
59
60 class HeaderParser:
61 def __init__(self):
62 self.model_body = None
63 self.cursor = 0
64 self.problems = []
65 def parse(self, model_body):
66 result = Header()
67 self.order = []
68 self.cursor = -1
69 self.model_body = model_body
70 self.skip_to_next()
71 result.description = self.current()
72 self.skip_to_next()
73 result.name = self.parse_pattern('^Name: (.+)$', 'name')[0]
74 self.skip_to_next()
75 result.author = self.parse_pattern('^Author: (.+)$', 'author')[0]
76 for header_entry in self.get_more_header_stuff():
77 if self.try_to_match(
78 '^!LDRAW_ORG ' +
79 '(' \
80 '(?:Unofficial_)?' \
81 'Part|' \
82 'Subpart|' \
83 'Primitive|' \
84 '8_Primitive|' \
85 '48_Primitive|' \
86 'Shortcut' \
87 ')\s?' \
88 '(ORIGINAL|UPDATE \d\d\d\d-\d\d)?$',
89 'part type'):
90 result.filetype, result.qualifiers = self.groups
91 elif self.try_to_match(
92 '^!LICENSE (.+)$',
93 'license'):
94 result.license = self.groups()
95 else:
96 self.parse_error("couldn't handle header metacommand: " + repr(header_entry.text))
97 return {
98 'header': result,
99 'end-index': self.cursor + 1,
100 }
101 def parse_error(self, message):
102 raise HeaderError(index = self.cursor, reason = message)
103 def get_more_header_stuff(self):
104 while True:
105 self.cursor += 1
106 if self.cursor >= len(self.model_body):
107 break
108 entry = self.model_body[self.cursor]
109 if not is_suitable_header_object(entry):
110 break
111 if isinstance(entry, linetypes.MetaCommand):
112 yield entry
113 def skip_to_next(self, *, spaces_expected = 0):
114 while True:
115 if self.cursor + 1 >= len(self.model_body):
116 self.parse_error('stub ldraw file')
117 self.cursor += 1
118 entry = self.model_body[self.cursor]
119 if not is_suitable_header_object(entry):
120 self.parse_error('header is incomplete')
121 if isinstance(entry, linetypes.MetaCommand):
122 return
123 def try_to_match(self, pattern, patterntype):
124 try:
125 self.groups = self.parse_pattern(pattern, patterntype)
126 except:
127 return False
128 def current(self):
129 entry = self.model_body[self.cursor]
130 assert isinstance(entry, linetypes.MetaCommand)
131 return entry.text
132 def parse_pattern(self, pattern, description):
133 match = re.search(pattern, self.current())
134 if match:
135 self.order.append(description)
136 return match.groups()
137 else:
138 self.parse_error(str.format("couldn't parse {}", description))

mercurial