iutest_pp_strip   F
last analyzed

Complexity

Total Complexity 155

Size/Duplication

Total Lines 626
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 553
dl 0
loc 626
rs 2
c 0
b 0
f 0
wmc 155

28 Methods

Rating   Name   Duplication   Size   Complexity  
A IutestPreprocessor.set_has_include() 0 2 1
A IutestPreprocessor.set_has_features() 0 2 1
A IutestPreprocessor.set_expands_macros() 0 2 1
A IutestPreprocessor.set_iutest_config_macro() 0 3 1
A IutestPreprocessor.set_predefined_macros() 0 2 1
A IutestPreprocessor.set_expand_function_macros() 0 2 1
A IutestPreprocessor.__init__() 0 13 1
A IutestPreprocessor.set_debug_flag() 0 2 1
A IutestPreprocessor.__expand_macro() 0 10 4
A IutestPreprocessor.__strip_namespace_iutest() 0 3 1
A IutestPreprocessor.__get_current_macro() 0 7 4
A IutestPreprocessor.__get_ppif_type() 0 10 5
A IutestPreprocessor.__check_include() 0 9 3
A IutestPreprocessor.__strip_namespaces() 0 4 1
D IutestPreprocessor.__expand_function_macro() 0 29 12
F IutestPreprocessor.remove_empty_ppif() 0 43 15
A IutestPreprocessor.__eval_ppif_unknown_defined_() 0 15 4
A IutestPreprocessor.__strip_namespace_iutest_detail() 0 3 1
F IutestPreprocessor.__expand_ppif_macro() 0 60 21
F IutestPreprocessor.__check_pp() 0 63 19
A IutestPreprocessor.__has_current_macro() 0 7 4
A IutestPreprocessor.__append_define() 0 18 5
A IutestPreprocessor.__strip_namespace() 0 13 5
A IutestPreprocessor.__eval_ppif_unknown_defined() 0 9 3
D IutestPreprocessor.__check_ppif() 0 27 13
F IutestPreprocessor.preprocess() 0 41 16
B IutestPreprocessor.__eval_ppif() 0 32 8
B IutestPreprocessor.__reduction() 0 118 3

How to fix   Complexity   

Complexity

Complex classes like iutest_pp_strip 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
2
#
3
# iutest_pp_strip.py
4
#
5
# Copyright (C) 2018, Takazumi Shirayanagi
6
# This software is released under the new BSD License,
7
# see LICENSE
8
#
9
10
import re
11
import collections
12
13
RE_MACRO_SPLIT = re.compile('([\(\):;{} /%+\-=<>!&\|*#]+)')
14
RE_SPLIT_PAREN = re.compile('([\(\)])')
15
RE_FUNC_MACRO = re.compile('([\w_]+)\((.*?)\)')
16
RE_DEFINE = re.compile('#\s*define (\S+)\s*(.*)$')
17
RE_DEFINE_PARSE = re.compile('(.*)defined\((.*?)\)(.*)')
18
RE_HAS_INCLUDE = re.compile('(.*)__has_include\((.*?)\)(.*)')
19
RE_HAS_FEATURE = re.compile('(.*)__has_feature\((.*?)\)(.*)')
20
RE_SPLIT_OP = re.compile('(&&|\|\||!)')
21
RE_SYMBOLMARK = re.compile('([+\-=<>\(\)]+)')
22
RE_PPIF = re.compile('#\s*(ifdef|ifndef|if)\s*(.*)$')
23
RE_PPELIF = re.compile('#\s*elif\s*(.*)$')
24
RE_PPELSE = re.compile('#\s*else\s*$')
25
RE_PPENDIF = re.compile('#\s*endif')
26
RE_CPP_COMMENT = re.compile('^//.*')
27
RE_SYSTEM_INCLUDE_REGEX = re.compile(r'^\s*#\s*include\s*<(.*)>')
28
29
RE_STRIP_INCG_REGEX = re.compile(r'^INCG_\S*_[IH]PP_\S+\Z')
30
31
RE_EVAL_UNDEFINED_EXCEPTION = re.compile(r'^name \'(defined_.*)\' is not defined\Z')
32
33
34
class IutestPreprocessor:
35
    macros = {}
36
    depth_macros = [ {} ]
37
    expands_macros = []
38
    expand_function_macros = []
39
    iutest_config_macro = []
40
    included_path = [ [] ]
41
    has_include = {}
42
    has_features = {}
43
    unknowns = []
44
    depth = []
45
    brothers = []
46
    debug = False
47
    prev_line = None
48
49
    def __init__(self
50
            , predefined_macros
51
            , iutest_config_macro
52
            , expand_function_macros
53
            , expands_macros
54
            , has_features
55
            , has_include):
56
        self.set_predefined_macros(predefined_macros)
57
        self.set_iutest_config_macro(iutest_config_macro)
58
        self.set_expand_function_macros(expand_function_macros)
59
        self.set_expands_macros(expands_macros)
60
        self.set_has_features(has_features)
61
        self.set_has_include(has_include)
62
63
    def set_predefined_macros(self, predefined_macros):
64
        self.macros = predefined_macros
65
66
    def set_iutest_config_macro(self, iutest_config_macro):
67
        self.iutest_config_macro = iutest_config_macro
68
        self.macros.update(iutest_config_macro)
69
70
    def set_expands_macros(self, expands_macros):
71
        self.expands_macros = expands_macros
72
73
    def set_expand_function_macros(self, expand_function_macros):
74
        self.expand_function_macros = expand_function_macros
75
76
    def set_has_include(self, has_include):
77
        self.has_include = has_include
78
79
    def set_has_features(self, has_features):
80
        self.has_features = has_features
81
82
    def set_debug_flag(self, flag):
83
        self.debug = flag
84
85
    def __expand_macro(self, line):
86
        dst = ""
87
        for s in RE_MACRO_SPLIT.split(line):
88
            if s in self.expands_macros:
89
                expand = self.__get_current_macro(s)
90
                if expand is not None:
91
                    dst += expand
92
                    continue
93
            dst += s
94
        return self.__expand_function_macro(dst)
95
96
    def __expand_function_macro(self, line):
97
        dst = ""
98
        tokens = []
99
        prev = ""
100
        for s in RE_SPLIT_PAREN.split(line):
101
            if s == '(':
102
                tokens.append(prev)
103
            elif s == ')' and len(tokens) > 0:
104
                tokens[-1] += prev + s
105
                s = ""
106
                ss = tokens.pop()
107
                for m in RE_FUNC_MACRO.finditer(ss):
108
                    d = m.group(1)
109
                    if d in self.expand_function_macros:
110
                        if d not in self.macros or self.macros[d] is None:
111
                            ss = ss.replace(m.group(0), '')
112
                if len(tokens) > 0:
113
                    tokens[-1] += ss
114
                else:
115
                    dst += ss
116
            elif len(tokens) > 0:
117
                tokens[-1] += prev
118
            else:
119
                dst += prev
120
            prev = s
121
        for s in tokens:
122
            dst += s
123
        dst += prev
124
        return dst
125
126
    def __has_current_macro(self, name):
127
        if name in self.macros:
128
            return True
129
        for m in self.depth_macros:
130
            if name in m:
131
                return True
132
        return False
133
134
    def __get_current_macro(self, name):
135
        if name in self.macros:
136
            return self.macros[name]
137
        for m in self.depth_macros:
138
            if name in m:
139
                return m[name]
140
        return None
141
142
    def __append_define(self, line):
143
        def append(d, v, depth, macros, unknowns, current):
144
            d = re.sub('\(.*\)', '', d)
145
            if len(v) == 0:
146
                if RE_STRIP_INCG_REGEX.match(d):
147
                    self.expands_macros.append(d)
148
                v = 'defined'
149
            if any(x == -1 for x in depth):
150
                unknowns.append(d)
151
                current[d] = v
152
            else:
153
                macros[d] = v
154
            return d
155
        m = RE_DEFINE.match(line)
156
        if m:
157
            current_depth_macro = self.depth_macros[-1]
158
            return append(m.group(1), m.group(2), self.depth, self.macros, self.unknowns, current_depth_macro)
159
        return None
160
161
    def __expand_ppif_macro(self, expr):
162
        expand = ""
163
        for s in RE_SPLIT_OP.split(expr):
164
            if s == '&&':
165
                expand += ' and '
166
            elif s == '||':
167
                expand += ' or '
168
            elif s == '!':
169
                expand += " not "
170
            else:
171
                m = RE_DEFINE_PARSE.match(s)
172
                if m:
173
                    d = m.group(2)
174
                    if self.__has_current_macro(d):
175
                        expand += m.group(1)
176
                        if self.__get_current_macro(d) is None:
177
                            expand += ' (0) '
178
                        else:
179
                            expand += ' (1) '
180
                        expand += m.group(3)
181
                    elif d in self.unknowns:
182
                        expand += s
183
                    else:
184
                        expand += s
185
                        self.unknowns.append(d)
186
                    continue
187
                m = RE_HAS_INCLUDE.match(s)
188
                if m:
189
                    f = m.group(2)
190
                    if f in self.has_include:
191
                        expand += m.group(1) + self.has_include[f] + m.group(3)
192
                    else:
193
                        expand += s
194
                    continue
195
                m = RE_HAS_FEATURE.match(s)
196
                if m:
197
                    f = m.group(2)
198
                    if f in self.has_features:
199
                        expand += m.group(1) + self.has_features[f] + m.group(3)
200
                        continue
201
                for w in RE_SYMBOLMARK.split(s.strip()):
202
                    if RE_SYMBOLMARK.match(w) or w.isspace():
203
                        expand += w
204
                    elif len(w) > 0:
205
                        if w in self.unknowns:
206
                            expand += w
207
                        elif w in self.macros:
208
                            if self.macros[w] is None:
209
                                expand += '0'
210
                            else:
211
                                expand += self.__expand_ppif_macro(self.macros[w])
212
                        elif w.isdigit():
213
                            expand += w
214
                        else:
215
                            expand += w
216
217
        expand = expand.replace('0(0)', '(0)')
218
        expand = expand.replace('not =', '!=')
219
        expand = re.sub(r'not\s*\(0\)', '(1)', expand)
220
        return expand.strip()
221
222
    def __eval_ppif_unknown_defined(self, expand):
223
        if 'defined' not in expand:
224
            return -1
225
        expand = re.sub(r'defined\((.*?)\)', 'defined_\\1', expand)
226
        try:
227
            r = eval(expand)
228
        except Exception as e:
229
            r = self.__eval_ppif_unknown_defined_(str(e), expand)
230
        return r
231
232
    def __eval_ppif_unknown_defined_(self, exception_str, expand):
233
        def eval_x(d, x):
234
            try:
235
                expanded = expand.replace(d, str(x))
236
                return eval(expanded)
237
            except Exception:
238
                return -1
239
        m = RE_EVAL_UNDEFINED_EXCEPTION.match(exception_str)
240
        if m:
241
            d = m.group(1)
242
            r0 = eval_x(d, 0)
243
            r1 = eval_x(d, 1)
244
            if r0 == r1:
245
                return r0
246
        return -1
247
248
    def __eval_ppif(self, expr):
249
        expand = self.__expand_ppif_macro(expr)
250
        expand_expr = re.sub(r'([0-9])+L', r'\1', expand)
251
        try:
252
            r = eval(expand_expr)
253
            if r:
254
                return (1, '1')
255
            else:
256
                return (0, '0')
257
        except Exception as e:
258
            r = -1
259
            if len(expand.split()) > 1:
260
                # r = self.__eval_ppif_unknown_defined(expand_expr)
261
                if r == -1:
262
                    if self.debug:
263
                        print(expr)
264
                        print(expand)
265
                        print(e)
266
                # strip fixed condition
267
                if '(0)' in expand or '(1)' in expand:
268
                    expand = re.sub(r'\s*\(0\)\s*or\s*', '', expand)
269
                    expand = re.sub(r'\s*or\s*\(0\)\s*', '', expand)
270
                    expand = re.sub(r'\s*\(1\)\s*and\s*', '', expand)
271
                    expand = re.sub(r'\s*and\s*\(1\)\s*', '', expand)
272
                    expand = expand.replace('and', '&&')
273
                    expand = expand.replace('or',  '||')
274
                    expand = expand.replace('not',  '!')
275
                    expand = expand.replace('(0)',  '0')
276
                    expand = expand.replace('(1)',  '1')
277
                    expand = expand.replace(' ', '')
278
                    return (r, expand)
279
            return (r, None)
280
281
    def __check_ppif(self, ins, expr):
282
        if ins == "if" or ins == "elif":
283
            return self.__eval_ppif(expr)
284
        else:
285
            def other():
286
                if ins == "ifdef":
287
                    if expr in self.unknowns:
288
                        return -1
289
                    elif expr not in self.macros:
290
                        return -1
291
                    elif expr in self.macros:
292
                        if self.macros[expr] is None:
293
                            return 0
294
                        return 1
295
                elif ins == "ifndef":
296
                    if expr in self.unknowns:
297
                        return -1
298
                    elif expr in self.macros:
299
                        if self.macros[expr] is None:
300
                            return 1
301
                        return 0
302
                    elif expr in self.expands_macros:
303
                        return 0
304
                    else:
305
                        return -1
306
                return -1
307
            return other(), None
308
309
    # return line string or None
310
    def __check_pp(self, line):
311
        def ret(b):
312
            if b:
313
                return line
314
            return None
315
        m = RE_PPIF.match(line)
316
        if m:
317
            expr = m.group(2)
318
            f,expanded_expr = self.__check_ppif(m.group(1), expr)
319
            self.depth.append(f)
320
            self.depth_macros.append({})
321
            self.included_path.append([])
322
            self.brothers.append([])
323
            if expanded_expr is not None:
324
                line = line.replace(expr, expanded_expr)
325
            return ret(all(x != 0 for x in self.depth) and f == -1)
326
        m = RE_PPELIF.match(line)
327
        if m:
328
            brother = self.brothers[-1]
329
            prev_brother_f = self.depth[-1]
330
            if len(brother) == 0 and prev_brother_f == 0:
331
                # Convert to #if if the last is if and the result is False
332
                line = line.replace('#elif', '#if')
333
            else:
334
                brother.append(prev_brother_f)
335
            f = 0
336
            if not any(x == 1 for x in brother):
337
                expr = m.group(1)
338
                f,expanded_expr = self.__check_ppif("elif", expr)
339
                if expanded_expr is not None:
340
                    line = line.replace(expr, expanded_expr)
341
            self.depth[-1] = f
342
            if all(x != 0 for x in self.depth):
343
                if f == -1 or any(x == -1 for x in brother):
344
                    return line
345
            return None
346
        m = RE_PPELSE.match(line)
347
        if m:
348
            brother = self.brothers[-1]
349
            brother.append(self.depth[-1])
350
            f = -1
351
            if f == 1 or any(x == 1 for x in brother):
352
                f = 0
353
            elif all(x == 0 for x in brother):
354
                f = 1
355
            self.depth[-1] = f
356
            return ret(all(x != 0 for x in self.depth) and f == -1)
357
        if RE_PPENDIF.match(line):
358
            brother = self.brothers[-1]
359
            f = self.depth.pop()
360
            self.included_path.pop()
361
            poped_macros = self.depth_macros.pop()
362
            b1 = all(x != 0 for x in self.depth)
363
            b2 = any(x == -1 for x in brother)
364
            self.brothers.pop()
365
            need_endif = b1 and (f == -1 or b2)
366
            if need_endif:
367
                return line
368
            if len(self.depth_macros) > 0:
369
                current_depth_macros = self.depth_macros[-1]
370
                current_depth_macros.update(poped_macros)
371
            return None
372
        return ret(len(self.depth) == 0 or all(x != 0 for x in self.depth))
373
374
    def __check_include(self, line):
375
        m = RE_SYSTEM_INCLUDE_REGEX.match(line)
376
        if m:
377
            path = m.group(1)
378
            if path in self.included_path[-1]:
379
                return False
380
            else:
381
                self.included_path[-1].append(path)
382
        return True
383
384
    def __reduction(self, line):
385
        reduction_macros = {
386
            'IP_INC':    'IP_I',
387
            'IP_DEC':    'IP_D',
388
            'IP_BOOL':   'IP_B',
389
            'IP_ENUM':   'IP_E',
390
            'IP_REPEAT': 'IP_R',
391
            'IP_IS_EMPTY_': 'IP_IS_EMP_',
392
            'IP_EMPTY_TAG': 'IP_EMP_T',
393
            'IP_IS_BEGIN_PARENS': 'IP_IS_BGN_P',
394
            'IP_E_PARAMS_MACRO_': 'IP_E_P_M_',
395
            'IP_E_P_M_IN_BINARY_': 'IP_E_P_M_B_',
396
            'IP_E_BINARY_PARAMS_': 'IP_E_B_P_',
397
            'IP_E_B_P_MACRO_': 'IP_E_B_P_M_',
398
            'IP_R_PARAMS_MACRO_': 'IP_R_P_M_',
399
            'IP_R_P_M_IN_BINARY_': 'IP_R_P_M_B_',
400
            'IP_R_BINARY_PARAMS_': 'IP_R_B_P_',
401
            'IP_R_B_P_MACRO_': 'IP_R_B_P_M_',
402
            'II_SHOW_MACRO':  'II_S_M',
403
            'II_SHOW_ENABLE_MACRO':   'II_S_E_M',
404
            'II_SHOW_DISABLE_MACRO':  'II_S_D_M',
405
            'II_SHOW_FEATURE_MACROS': 'II_S_F_M',
406
            'II_ELEMENTSARE': 'II_EA',
407
            'II_EA_MATCHER_NAME': 'II_EA_M_N',
408
            'II_ANYOF_AND_ALLOF_MATCHER_NAME': 'II_AAA_M_N',
409
            'II_DECL_VALUEARRAY_': 'II_D_VA_',
410
            'II_DECL_CARTESIAN_PRODUCT_': 'II_D_C_P_',
411
            'II_DECL_PAIRWISE_': 'II_D_PW_',
412
            'II_DECL_IS_FUNCTION_PTR_': 'II_D_IS_FP_',
413
            'II_DECL_IS_MEMBER_FUNCTION_PTR_': 'II_D_IS_M_FP_',
414
            'II_DECL_FUNCTION_RETURN_TYPE_': 'II_D_F_R_T_',
415
            'II_DECL_EXPRESSION_': 'II_D_EP_',
416
            'II_DECL_ELEMENTSARE': 'II_D_EA',
417
            'II_DECL_TUPLE_PRINTTO': 'II_D_T_PT',
418
            'II_DECL_ANYOF_AND_ALLOF': 'II_D_AAA',
419
            'II_DECL_COMPARE_HELPER_': 'II_D_C_H_',
420
            'II_DECL_COMBINE_':   'II_D_C_',
421
            'II_DECL_VALUES_':    'II_D_V_',
422
            'II_DECL_TYPES_':     'II_D_T_',
423
            'II_DECL_TEMPLATES_': 'II_D_TPL_',
424
            'II_DECL_TYPELIST_':  'II_D_TL_',
425
            'II_DECL_TEMPLATETYPELIST_': 'II_D_TTL_',
426
            'II_DECL_PEEP_MEMBER_FUNC_': 'II_D_PE_M_F_',
427
            'II_DECL_COMPARE_MATCHER': 'II_D_COMP_M',
428
            'II_DECL_STR_COMPARE_MATCHER': 'II_D_S_COMP_M',
429
            'II_DECL_ALLOF_MATCHER': 'II_D_ALL_M',
430
            'II_DECL_ANYOF_MATCHER': 'II_D_ANY_M',
431
            'II_DECL_DEFAULT_ARG_': 'II_D_DEF_A_',
432
            'II_DECL_SPEC_NONE_': 'II_D_S_NN_',
433
            'II_SUCCEED': 'II_S',
434
            'II_FAIL': 'II_F',
435
            'II_ADD_FAILURE': 'II_ADD_F',
436
            'II_SCOPED_MESSAGE': 'II_S_MSG',
437
            'II_D_C_P_GENERATOR_': 'II_D_C_P_GEN_',
438
            'II_D_C_P_HOLDER_': 'II_D_C_P_HLR_',
439
            'II_D_PW_GENERATOR_': 'II_D_PW_GEN_',
440
            'II_D_PW_HOLDER_': 'II_D_PW_HLR_',
441
            'II_CONCAT_PACKAGE': 'II_CC_PKG',
442
            'II_PACKAGE_': 'II_PKG_',
443
            'II_PKG_CURRENT_NAMESPACE_': 'II_PKG_C_NS_',
444
            'II_PKG_PARENT_NAMESPACE_':  'II_PKG_P_NS_',
445
            'II_TEST_CLASS_NAME_': 'II_T_C_N_',
446
            'II_TEST_INSTANCE_NAME_': 'II_T_INST_N_',
447
            'II_TO_VARNAME_': 'II_TO_VN_',
448
            'II_TO_NAME_': 'II_TO_N_',
449
            'II_CHECK_TESTFIXTURE': 'II_CK_TF',
450
            'II_PMZ_TEST_CLASS_NAME_': 'II_PMZ_T_C_N_',
451
            'II_GETTESTCASEPATTERNHOLDER': 'II_GTCPH',
452
            'II_INSTANTIATE_TEST_CASE_P_': 'II_INST_TC_P_',
453
            'II_TEST_P_EVALGENERATOR_NAME_':    'II_T_P_EGEN_N_',
454
            'II_TEST_P_PARAMGENERATOR_NAME_':   'II_T_P_PGEN_N_',
455
            'II_TEST_P_INSTANTIATIONREGISTER_': 'II_T_P_INST_R_',
456
            'II_TEST_P_FIXTURE_DECL_': 'II_T_P_FX_D_',
457
            'II_TEST_P_BASE_FIXTURE': 'II_T_P_B_FX',
458
            'II_T_P_INST_R_NAME_': 'II_T_P_INST_R_N_',
459
            'II_ALIAS_TESTNAME_PP_': 'II_A_TN_PP_',
460
            'II_TEST_EXPRESSION_': 'II_T_EXPR_',
461
            'II_T_EXPR_EXPAND_EXPRESSION': 'II_T_EXPR_E_E',
462
            'II_EXPRESSION_DECOMPOSE': 'II_EXPR_DEC',
463
            'II_D_EP_RESULT_OP': 'II_D_EP_R_OP',
464
            'II_TYPED_TEST_': 'II_T_T_',
465
            'II_T_T_CASE_': 'II_T_TC_',
466
            'II_T_TC_PSTATE_NAME_': 'II_T_TC_PS_N_',
467
            'II_T_T_P_NAMESPACE_': 'II_T_T_P_NS_',
468
            'II_T_T_P_ADDTESTNAME': 'II_T_T_P_ADD_TN',
469
            'II_T_T_PARAMS_': 'II_T_T_PRMS_',
470
            'II_REGISTER_TYPED_TEST_CASE_P_':    'II_R_T_TC_P_',
471
            'II_INSTANTIATE_TYPED_TEST_CASE_P_': 'II_INST_T_TC_P_',
472
            'II_PEEP_TAG_NAME_':    'II_PE_T_N_',
473
            'II_PEEP_SETTER_NAME_': 'II_PE_S_N_',
474
            'II_GeTypeNameSpecialization': 'II_GTNS',
475
            'II_WORKAROUND_GENRAND': 'II_WA_GENRAND',
476
        }
477
        line = line.replace('IIUT_', 'II_')
478
        line = line.replace('II_PP_', 'IP_')
479
        line = line.replace('IUTEST_UNUSED_VAR', '(void)')
480
        line = line.replace('statement', 'st')
481
        line = line.replace('expected_exception', 'exp_e')
482
        line = line.replace('exp_e_value', 'exp_e_v')
483
        line = line.replace('expected_str',   'exp_s')
484
        line = line.replace('expected_value', 'exp_v')
485
        line = line.replace('actual_str',  'act_s')
486
        line = line.replace('regex_str',  'regex_s')
487
        line = line.replace('pred_formatter',  'pd_fmt')
488
        line = line.replace('on_failure',  'on_f')
489
        line = line.replace('testcasename_',  'tcn_')
490
        line = line.replace('testname_',  'tn_')
491
        line = line.replace('testfixture_',  'tf_')
492
        for k,v in reduction_macros.items():
493
            if collections.Counter(reduction_macros.values())[v] > 1:
494
                print('error: duplicated ' + v)
495
                continue
496
            line = line.replace(k, v)
497
        line = re.sub(r'(?<![\w\d_])NULL(?![\w\d_])', '0', line)
498
        # line = re.sub('\s+', ' ', line)
499
        line = re.sub('\s$', '', line)
500
        line = line.strip()
501
        return line
502
503
    def __strip_namespace(self, line, ns):
504
        s = ""
505
        for n in ns:
506
            s += "namespace " + n + "{"
507
        e = ""
508
        for n in ns:
509
            e += "}"
510
        def __is_namespace_open_close_line(x):
511
            return x.startswith(s) and x.endswith(e)
512
        if __is_namespace_open_close_line(line) and __is_namespace_open_close_line(self.prev_line):
513
            self.prev_line = self.prev_line[:-len(e)]
514
            line = line[len(s):]
515
        return line
516
517
    def __strip_namespace_iutest_detail(self, line):
518
        ns = ['iutest', 'detail']
519
        return self.__strip_namespace(line, ns)
520
521
    def __strip_namespace_iutest(self, line):
522
        ns = ['iutest']
523
        return self.__strip_namespace(line, ns)
524
525
    def __strip_namespaces(self, line):
526
        line = self.__strip_namespace_iutest_detail(line)
527
        line = self.__strip_namespace_iutest(line)
528
        return line
529
530
    def preprocess(self, code, add_macros):
531
        if add_macros is not None:
532
            self.macros.update(add_macros)
533
        dst = ""
534
        for line in code.splitlines():
535
            # c++ comment
536
            if RE_CPP_COMMENT.match(line):
537
                continue
538
            # if/ifdef/ifndef/elif/endif
539
            line = self.__check_pp(line)
540
            if line:
541
                # include
542
                if not self.__check_include(line):
543
                    continue
544
                # define
545
                d = self.__append_define(line)
546
                if d:
547
                    # config macro insert
548
                    if 'INCG_IRIS_IUTEST_CONFIG_HPP_' in d:
549
                        dst += self.prev_line + '\n'
550
                        self.prev_line = None
551
                        for k,v in self.iutest_config_macro.items():
552
                            dst += '#define ' + k + ' ' + str(v) + '\n'
553
                        self.iutest_config_macro = []
554
                    if d in self.expands_macros or d in self.expand_function_macros:
555
                        continue
556
                    if d in ['IUTEST_UNUSED_VAR']:
557
                        continue
558
                line = self.__expand_macro(line)
559
                if len(line) > 0:
560
                    line = self.__reduction(line)
561
                    if self.prev_line is not None:
562
                        line = self.__strip_namespaces(line)
563
                        if self.prev_line.startswith('#'):
564
                             self.prev_line += '\n'
565
                        elif line.startswith('#'):
566
                             self.prev_line += '\n'
567
                        dst += self.prev_line
568
                    self.prev_line = line
569
        dst += self.prev_line + '\n'
570
        return dst
571
572
    def __get_ppif_type(self, line):
573
        if RE_PPIF.match(line):
574
            return 'if'
575
        elif RE_PPELIF.match(line):
576
            return 'elif'
577
        elif RE_PPELSE.match(line):
578
            return 'else'
579
        elif RE_PPENDIF.match(line):
580
            return 'endif'
581
        return None
582
583
    def remove_empty_ppif(self, code):
584
        dst = ""
585
        prev = None
586
        cache_lines = []
587
        def cach_clear():
588
            ret = ""
589
            for s in cache_lines:
590
                if s is not None:
591
                    ret += s
592
            del cache_lines[:]
593
            return ret
594
595
        for line in code.splitlines():
596
            line += "\n"
597
            t = self.__get_ppif_type(line)
598
            if t == 'endif':
599
                if prev == 'if':
600
                    if len(cache_lines) > 0:
601
                        cache_lines = cache_lines[:-1]
602
                elif prev != 'endif':
603
                    if len(cache_lines) > 0:
604
                        cache_lines[-1] = line
605
                    else:
606
                        cache_lines.append(line)
607
                else:
608
                    dst += cach_clear()
609
                    dst += line
610
            elif t is not None:
611
                if prev is None:
612
                    cache_lines.append(line)
613
                else:
614
                    if t == 'else' and prev == 'elif':
615
                        cache_lines[-1] = line
616
                    elif t == 'elif' and prev == 'elif':
617
                        cache_lines[-1] = line
618
                    else:
619
                        cache_lines.append(line)
620
            else:
621
                dst += cach_clear()
622
                dst += line
623
            prev = t
624
        dst += cach_clear()
625
        return dst
626