header.py

changeset 48
38b0919c1934
parent 47
4da025d0b283
child 49
a1f5c12fa45c
equal deleted inserted replaced
47:4da025d0b283 48:38b0919c1934
1 import re 1 import re
2 import linetypes 2 import linetypes
3 import datetime
3 4
4 class Header: 5 class Header:
5 def __init__(self): 6 def __init__(self):
6 self.description = None 7 self.description = None
7 self.name = None 8 self.name = None
8 self.author = None 9 self.author = None
9 self.username = None 10 self.username = None
10 self.filetype = None 11 self.filetype = None
11 self.qualifiers = None 12 self.qualifiers = None
12 self.license = None 13 self.license = None
13 self.help = [] 14 self.help = ''
14 self.bfc = None 15 self.bfc = None
15 self.category = None 16 self.category = None
16 self.keywords = [] 17 self.keywords = ''
17 self.cmdline = None 18 self.cmdline = None
18 self.history = [] 19 self.history = []
20 self.first_occurrence = dict()
21 @property
22 def valid(self):
23 return True
19 24
20 class BadHeader: 25 class BadHeader:
21 def __init__(self, index, reason): 26 def __init__(self, index, reason):
22 self.index = index 27 self.index = index
23 self.reason = reason 28 self.reason = reason
25 return str.format( 30 return str.format(
26 'header.BadHeader(index = {index!r}, reason = {reason!r})', 31 'header.BadHeader(index = {index!r}, reason = {reason!r})',
27 index = self.index, 32 index = self.index,
28 reason = self.reason, 33 reason = self.reason,
29 ) 34 )
35 @property
36 def valid(self):
37 return False
30 38
31 geometrical_types = [ 39 geometrical_types = [
32 linetypes.LineSegment, 40 linetypes.LineSegment,
33 linetypes.Triangle, 41 linetypes.Triangle,
34 linetypes.Quadrilateral, 42 linetypes.Quadrilateral,
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))

mercurial