Completed
Push — master ( 4d207a...61d2e5 )
by srz
01:21
created

PaizaPreprocessor.preprocess()   C

Complexity

Conditions 9

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 9
dl 0
loc 20
rs 6.4615
1
#!/usr/bin/env python
2
#
3
# iupaiza_pp.py
4
#
5
6
import re
7
8
predefined_macros = {
9
					'__clang__': '1',
10
					'_LIBCPP_VERSION': '1101',
11
					'NULL': '0',
12
					'__linux__': '1',
13
					'__cplusplus': '201402',
14
					'__cpp_rvalue_references': '200610',
15
					'__has_include': None
16
					}
17
#userdefined_macros = { '': '1'
18
#					}
19
expands_macros = [
20
				'IUTEST_IPP_INLINE',
21
				'IUTEST_NULLPTR',
22
				'IUTEST_CXX_CONSTEXPR',
23
				'IUTEST_CXX_CONSTEXPR_OR_CONST',
24
				'IUTEST_CXX_DELETED_FUNCTION',
25
				'IUTEST_CXX_DEFAULT_FUNCTION',
26
				'IUTEST_CXX_EXPLICIT_CONVERSION',
27
				'IUTEST_CXX_NOEXCEPT_SPEC',
28
				'IUTEST_CXX_OVERRIDE',
29
				'IUTEST_CXX_FINAL',
30
				'IUTEST_CXX_NOTHROW',
31
				'IUTEST_PRAGMA_GCC_WARN_PUSH',
32
				'IUTEST_PRAGMA_GCC_WARN_DISABLE',
33
				'IUTEST_PRAGMA_GCC_WARN_POP',
34
				'IUTEST_ATTRIBUTE_UNUSED_',
35
				'IUTEST_ATTRIBUTE_DEPRECATED_',
36
				'IUTEST_ATTRIBUTE_PURE_',
37
				'IUTEST_ATTRIBUTE_NORETURN_'
38
				]
39
40
#
41
expand_function_macros = [
42
						'IUTEST_PRAGMA_CRT_SECURE_WARN_DISABLE_BEGIN',
43
						'IUTEST_PRAGMA_CRT_SECURE_WARN_DISABLE_END',
44
						'IUTEST_PRAGMA_EXTERN_TEMPLATE_WARN_DISABLE_BEGIN',
45
						'IUTEST_PRAGMA_EXTERN_TEMPLATE_WARN_DISABLE_END',
46
						'IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_BEGIN',
47
						'IUTEST_PRAGMA_CONSTEXPR_CALLED_AT_RUNTIME_WARN_DISABLE_END',
48
						'IUTEST_PRAGMA_MSC_WARN_PUSH',
49
						'IUTEST_PRAGMA_MSC_WARN_DISABLE',
50
						'IUTEST_PRAGMA_MSC_WARN_POP',
51
						'IUTEST_WORKAROUND_MSC_STLSTREAM_C4250',
52
						'IUTEST_EXPLICIT_TEMPLATE_TYPE_',
53
						'IUTEST_APPEND_EXPLICIT_TEMPLATE_TYPE_'
54
						]
55
56
# 
57
clang_has_features = {
58
				'cxx_nullptr': '1',
59
				'cxx_attributes': '1',
60
				'cxx_auto_type': '1' ,
61
				'cxx_constexpr': '1',
62
				'cxx_decltype': '1',
63
				'cxx_defaulted_functions': '1',
64
				'cxx_deleted_functions': '1',
65
				'cxx_explicit_conversions': '1',
66
				'cxx_generalized_initializers': '1',
67
				'cxx_lambdas': '1',
68
				'cxx_noexcept': '1',
69
				'cxx_override_control': '1',
70
				'cxx_rtti': '1',
71
				'cxx_rvalue_references': '1',
72
				'cxx_static_assert': '1',
73
				'cxx_strong_enums': '1',
74
				'cxx_unicode_literals': '1',
75
				'cxx_variadic_templates': '1',
76
				'c_generic_selections':  '0'
77
				}
78
79
clang_has_include = {
80
					'<cxxabi.h>': '1',
81
#					'<uchar.h>': '1',
82
					'<uchar.h>': '0',
83
					'<experimental/any>': '0',
84
					'<ext/cmath>': '0',
85
					'<array>': '1',
86
					'<future>': '1',
87
					'<ratio>': '1',
88
					'<shared_mutex>': '1',
89
					'<scoped_allocator>': '1',
90
					'<typeindex>': '1',
91
					'<type_traits>': '1',
92
					'<tr1/tuple>': '0'
93
					}
94
95
RE_MACRO_SPLIT = re.compile('([\(\):;{} /%+\-=<>!&\|*#]+)')
96
RE_SPLIT_PAREN = re.compile('([\(\)])')
97
RE_FUNC_MACRO = re.compile('([\w_]+)\((.*?)\)')
98
RE_DEFINE = re.compile('#\s*define (\S+)\s*(.*)$')
99
RE_DEFINE_PARSE = re.compile('(.*)defined\((.*?)\)(.*)')
100
RE_HAS_INCLUDE = re.compile('(.*)__has_include\((.*?)\)(.*)')
101
RE_HAS_FEATURE = re.compile('(.*)__has_feature\((.*?)\)(.*)')
102
RE_SPLIT_OP = re.compile('(&&|\|\||!)')
103
RE_SYMBOLMARK = re.compile('([+\-=<>\(\)]+)')
104
RE_PPIF = re.compile('#\s*(ifdef|ifndef|if)\s*(.*)$')
105
RE_PPELIF = re.compile('#\s*elif(.*)$')
106
RE_PPELSE = re.compile('#\s*else\s*$')
107
RE_PPENDIF = re.compile('#\s*endif')
108
RE_CPP_COMMENT = re.compile('^//.*')
109
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