Total Complexity | 84 |
Total Lines | 213 |
Duplicated Lines | 0 % |
Complex classes like PaizaPreprocessor often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | #!/usr/bin/env python |
||
110 | class PaizaPreprocessor: |
||
111 | macros = predefined_macros |
||
112 | unkowns = [] |
||
113 | depth = [] |
||
114 | brothers = [] |
||
115 | |||
116 | |||
117 | def __expand_macro(self, line): |
||
118 | dst = "" |
||
119 | for s in RE_MACRO_SPLIT.split(line): |
||
120 | if s in expands_macros and s in self.macros: |
||
121 | if self.macros[s]: |
||
122 | dst += self.macros[s] |
||
123 | else: |
||
124 | dst += s |
||
125 | return self.__expand_function_macro(dst) |
||
126 | |||
127 | |||
128 | def __expand_function_macro(self, line): |
||
129 | dst = "" |
||
130 | tokens = [] |
||
131 | prev = "" |
||
132 | for s in RE_SPLIT_PAREN.split(line): |
||
133 | if s == '(': |
||
134 | tokens.append(prev) |
||
135 | elif s == ')' and len(tokens) > 0: |
||
136 | tokens[-1] += prev + s |
||
137 | s = "" |
||
138 | ss = tokens.pop() |
||
139 | for m in RE_FUNC_MACRO.finditer(ss): |
||
140 | d = m.group(1) |
||
141 | if d in expand_function_macros: |
||
142 | if d in self.macros and self.macros[d] == None: |
||
143 | ss = ss.replace(m.group(0), '') |
||
144 | if len(tokens) > 0: |
||
145 | tokens[-1] += ss |
||
146 | else: |
||
147 | dst += ss |
||
148 | elif len(tokens) > 0: |
||
149 | tokens[-1] += prev |
||
150 | else: |
||
151 | dst += prev |
||
152 | prev = s |
||
153 | for s in tokens: |
||
154 | dst += s |
||
155 | dst += prev |
||
156 | return dst |
||
157 | |||
158 | |||
159 | def __append_define(self, line): |
||
160 | def append(d, v, depth, macros, unkowns): |
||
161 | d = re.sub('\(.*\)', '', d) |
||
162 | if any(x == -1 for x in depth): |
||
163 | unkowns.append(d) |
||
164 | else: |
||
165 | if len(v) == 0: |
||
166 | macros[d] = None |
||
167 | else: |
||
168 | macros[d] = v |
||
169 | return d |
||
170 | m = RE_DEFINE.match(line) |
||
171 | if m: |
||
172 | return append(m.group(1), m.group(2), self.depth, self.macros, self.unkowns) |
||
173 | return None |
||
174 | |||
175 | def __expand_ppif_macro(self, expr): |
||
176 | expand = "" |
||
177 | for s in RE_SPLIT_OP.split(expr): |
||
178 | if s == '&&': |
||
179 | expand += ' and ' |
||
180 | elif s == '||': |
||
181 | expand += ' or ' |
||
182 | elif s == '!': |
||
183 | expand += " not " |
||
184 | else: |
||
185 | m = RE_DEFINE_PARSE.match(s) |
||
186 | if m: |
||
187 | d = m.group(2) |
||
188 | if d in self.unkowns: |
||
189 | expand += s |
||
190 | else: |
||
191 | f = d in self.macros |
||
192 | expand += m.group(1) + str(f) + m.group(3) |
||
193 | continue |
||
194 | m = RE_HAS_INCLUDE.match(s) |
||
195 | if m: |
||
196 | f = m.group(2) |
||
197 | if f in clang_has_include: |
||
198 | expand += m.group(1) + clang_has_include[f] + m.group(3) |
||
199 | else: |
||
200 | expand += s |
||
201 | continue |
||
202 | m = RE_HAS_FEATURE.match(s) |
||
203 | if m: |
||
204 | f = m.group(2) |
||
205 | if f in clang_has_features: |
||
206 | expand += m.group(1) + clang_has_features[f] + m.group(3) |
||
207 | continue |
||
208 | for w in RE_SYMBOLMARK.split(s): |
||
209 | if RE_SYMBOLMARK.match(w) or w.isspace(): |
||
210 | expand += w |
||
211 | elif len(w) > 0: |
||
212 | if w in self.unkowns: |
||
213 | expand += s |
||
214 | elif w in self.macros: |
||
215 | expand += self.__expand_ppif_macro(self.macros[w]) |
||
216 | elif w.isdigit(): |
||
217 | expand += w |
||
218 | else: |
||
219 | expand += '0' |
||
220 | |||
221 | expand = expand.replace('0(0)', '0') |
||
222 | expand = expand.replace('not =', '!=') |
||
223 | return expand |
||
224 | |||
225 | |||
226 | def __eval_ppif(self, expr): |
||
227 | expand = self.__expand_ppif_macro(expr) |
||
228 | try: |
||
229 | if eval(expand): |
||
230 | return 1 |
||
231 | else: |
||
232 | return 0 |
||
233 | except Exception, e: |
||
234 | if not any( x in expand for x in self.unkowns ): |
||
235 | if True: |
||
236 | print(expr) |
||
237 | print(expand) |
||
238 | print(e) |
||
239 | return -1 |
||
240 | |||
241 | |||
242 | def __check_ppif(self, ins, expr): |
||
243 | if ins == "if" or ins == "elif": |
||
244 | return self.__eval_ppif(expr) |
||
245 | elif ins == "ifdef": |
||
246 | if expr in self.unkowns: |
||
247 | return -1 |
||
248 | if expr not in self.macros: |
||
249 | return 0 |
||
250 | elif ins == "ifndef": |
||
251 | if expr in self.unkowns: |
||
252 | return -1 |
||
253 | if expr in self.macros: |
||
254 | return 0 |
||
255 | return 1 |
||
256 | |||
257 | |||
258 | def __check_pp(self, line): |
||
259 | m = RE_PPIF.match(line) |
||
260 | if m: |
||
261 | f = self.__check_ppif(m.group(1), m.group(2)) |
||
262 | self.depth.append(f) |
||
263 | self.brothers.append([]) |
||
264 | return all( x != 0 for x in self.depth ) and f == -1 |
||
265 | m = RE_PPELIF.match(line) |
||
266 | if m: |
||
267 | brother = self.brothers[-1] |
||
268 | brother.append(self.depth[-1]) |
||
269 | f = 0 |
||
270 | if not any( x == 1 for x in brother): |
||
271 | f = self.__check_ppif("elif", m.group(1)) |
||
272 | self.depth[-1] = f |
||
273 | return all( x != 0 for x in self.depth ) and any(x == -1 for x in brother) |
||
274 | m = RE_PPELSE.match(line) |
||
275 | if m: |
||
276 | brother = self.brothers[-1] |
||
277 | f = self.depth[-1] |
||
278 | if f == 1 or any(x == 1 for x in brother): |
||
279 | f = 0 |
||
280 | elif f == 0: |
||
281 | f = 1 |
||
282 | self.depth[-1] = f |
||
283 | return all( x != 0 for x in self.depth ) and f == -1 |
||
284 | if RE_PPENDIF.match(line): |
||
285 | brother = self.brothers[-1] |
||
286 | f = self.depth.pop() |
||
287 | b1 = all( x != 0 for x in self.depth ) |
||
288 | b2 = any(x == -1 for x in brother) |
||
289 | self.brothers.pop() |
||
290 | return b1 and (f == -1 or b2) |
||
291 | return len(self.depth) == 0 or all( x != 0 for x in self.depth ) |
||
292 | |||
293 | |||
294 | def __reduction(self, line): |
||
295 | line = line.replace('IIUT_', 'II_') |
||
296 | line = line.replace('II_PP_', 'IP_') |
||
297 | line = line.replace('IUTEST_UNUSED_VAR', '(void)') |
||
298 | line = re.sub('\s+', ' ', line) |
||
299 | line = re.sub('\s$', '', line) |
||
300 | return line |
||
301 | |||
302 | |||
303 | def preprocess(self, code, macros): |
||
304 | dst = "" |
||
305 | for line in code.splitlines(): |
||
306 | # c++ comment |
||
307 | if RE_CPP_COMMENT.match(line): |
||
308 | continue |
||
309 | # if/ifdef/ifndef/elif/endif |
||
310 | if self.__check_pp(line): |
||
311 | # define |
||
312 | d = self.__append_define(line) |
||
313 | if d: |
||
314 | if d in expands_macros or d in expand_function_macros: |
||
315 | continue |
||
316 | if d in [ 'IUTEST_UNUSED_VAR' ]: |
||
317 | continue |
||
318 | line = self.__expand_macro(line) |
||
319 | if len(line) > 0: |
||
320 | line = self.__reduction(line) |
||
321 | dst += line + "\n" |
||
322 | return dst |
||
323 |