| 55 reason = self.reason, |
63 reason = self.reason, |
| 56 ) |
64 ) |
| 57 def __str__(self): |
65 def __str__(self): |
| 58 return reason |
66 return reason |
| 59 |
67 |
| |
68 class HistoryEntry: |
| |
69 def __init__(self, date, user, text): |
| |
70 self.date, self.user, self.text = date, user, text |
| |
71 def __repr__(self): |
| |
72 return str.format( |
| |
73 'HistoryEntry({date!r}, {user!r}, {text!r})', |
| |
74 date = self.date, |
| |
75 user = self.user, |
| |
76 text = self.text) |
| |
77 |
| 60 class HeaderParser: |
78 class HeaderParser: |
| 61 def __init__(self): |
79 def __init__(self): |
| 62 self.model_body = None |
80 self.model_body = None |
| 63 self.cursor = 0 |
81 self.cursor = 0 |
| 64 self.problems = [] |
82 self.problems = [] |
| 65 def parse(self, model_body): |
83 def parse(self, model_body): |
| 66 result = Header() |
84 result = Header() |
| |
85 self.result = result |
| 67 self.order = [] |
86 self.order = [] |
| 68 self.cursor = -1 |
87 self.cursor = -1 |
| 69 self.model_body = model_body |
88 self.model_body = model_body |
| 70 self.skip_to_next() |
89 self.skip_to_next() |
| 71 result.description = self.current() |
90 result.description = self.current() |
| 72 self.skip_to_next() |
91 self.skip_to_next() |
| 73 result.name = self.parse_pattern('^Name: (.+)$', 'name')[0] |
92 result.name = self.parse_pattern('^Name: (.+)$', 'name')[0] |
| 74 self.skip_to_next() |
93 self.skip_to_next() |
| 75 result.author = self.parse_pattern('^Author: (.+)$', 'author')[0] |
94 result.author, result.username = self.parse_pattern(r'^Author: ([^\[]+)\s*(?:\[(.+)\])?$', 'author') |
| 76 for header_entry in self.get_more_header_stuff(): |
95 for header_entry in self.get_more_header_stuff(): |
| 77 if self.try_to_match( |
96 if self.try_to_match( |
| 78 '^!LDRAW_ORG ' + |
97 '^!LDRAW_ORG ' + |
| 79 '(' \ |
98 r'(' \ |
| 80 '(?:Unofficial_)?' \ |
99 '(?:Unofficial_)?' \ |
| 81 'Part|' \ |
100 'Part|' \ |
| 82 'Subpart|' \ |
101 'Subpart|' \ |
| 83 'Primitive|' \ |
102 'Primitive|' \ |
| 84 '8_Primitive|' \ |
103 '8_Primitive|' \ |
| 85 '48_Primitive|' \ |
104 '48_Primitive|' \ |
| 86 'Shortcut' \ |
105 'Shortcut' \ |
| 87 ')\s?' \ |
106 ')\s?' \ |
| 88 '(ORIGINAL|UPDATE \d\d\d\d-\d\d)?$', |
107 '(.*)$', |
| 89 'part type'): |
108 'part type'): |
| 90 result.filetype, result.qualifiers = self.groups |
109 result.filetype = self.groups[0] |
| |
110 result.qualifiers = re.findall(r'(?:Physical_Colour|Alias|ORIGINAL|UPDATE \d\d\d\d-\d\d)', self.groups[1]) |
| 91 elif self.try_to_match( |
111 elif self.try_to_match( |
| 92 '^!LICENSE (.+)$', |
112 '^!LICENSE (.+)$', |
| 93 'license'): |
113 'license'): |
| 94 result.license = self.groups() |
114 result.license = self.groups[0] |
| |
115 elif self.try_to_match( |
| |
116 'BFC (CERTIFY CW|CERTIFY CCW|NOCERTIFY)', |
| |
117 'bfc'): |
| |
118 result.bfc = self.groups[0] |
| |
119 elif self.try_to_match( |
| |
120 r'!HISTORY (\d{4}-\d{2}-\d{2}) ([\[{][^\]}]+[\]}]) (.+)$', |
| |
121 'history'): |
| |
122 result.history.append(HistoryEntry( |
| |
123 date = datetime.datetime.strptime(self.groups[0], '%Y-%m-%d').date(), |
| |
124 user = self.groups[1], |
| |
125 text = self.groups[2], |
| |
126 )) |
| |
127 elif self.try_to_match( |
| |
128 r'!HELP (.+)', |
| |
129 'help'): |
| |
130 if result.help: |
| |
131 result.help += '\n' |
| |
132 result.help += self.groups[0] |
| |
133 elif self.try_to_match( |
| |
134 r'!CATEGORY (.+)', |
| |
135 'category'): |
| |
136 result.category = self.groups[0] |
| |
137 elif self.try_to_match( |
| |
138 r'!KEYWORDS (.+)', |
| |
139 'keywords'): |
| |
140 if result.keywords: |
| |
141 result.keywords += '\n' |
| |
142 result.keywords += self.groups[0] |
| |
143 elif self.try_to_match( |
| |
144 r'!CMDLINE (.+)', |
| |
145 'cmdline'): |
| |
146 result.cmdline = self.groups[0] |
| 95 else: |
147 else: |
| 96 self.parse_error("couldn't handle header metacommand: " + repr(header_entry.text)) |
148 self.parse_error("couldn't understand header syntax: " + repr(header_entry.text)) |
| 97 return { |
149 return { |
| 98 'header': result, |
150 'header': result, |
| 99 'end-index': self.cursor + 1, |
151 'end-index': self.cursor + 1, |
| 100 } |
152 } |
| 101 def parse_error(self, message): |
153 def parse_error(self, message): |
| 121 if isinstance(entry, linetypes.MetaCommand): |
173 if isinstance(entry, linetypes.MetaCommand): |
| 122 return |
174 return |
| 123 def try_to_match(self, pattern, patterntype): |
175 def try_to_match(self, pattern, patterntype): |
| 124 try: |
176 try: |
| 125 self.groups = self.parse_pattern(pattern, patterntype) |
177 self.groups = self.parse_pattern(pattern, patterntype) |
| |
178 return True |
| 126 except: |
179 except: |
| 127 return False |
180 return False |
| 128 def current(self): |
181 def current(self): |
| 129 entry = self.model_body[self.cursor] |
182 entry = self.model_body[self.cursor] |
| 130 assert isinstance(entry, linetypes.MetaCommand) |
183 assert isinstance(entry, linetypes.MetaCommand) |
| 131 return entry.text |
184 return entry.text |
| 132 def parse_pattern(self, pattern, description): |
185 def parse_pattern(self, pattern, description): |
| 133 match = re.search(pattern, self.current()) |
186 match = re.search(pattern, self.current()) |
| 134 if match: |
187 if match: |
| 135 self.order.append(description) |
188 self.order.append(description) |
| |
189 if description not in self.result.first_occurrence: |
| |
190 self.result.first_occurrence[description] = self.cursor |
| 136 return match.groups() |
191 return match.groups() |
| 137 else: |
192 else: |
| 138 self.parse_error(str.format("couldn't parse {}", description)) |
193 self.parse_error(str.format("couldn't parse {}", description)) |