Author: Armin Rigo <ar...@tunes.org> Branch: cmacros Changeset: r2239:b9af889b244a Date: 2015-07-30 22:13 +0200 http://bitbucket.org/cffi/cffi/changeset/b9af889b244a/
Log: Reintroduce condition-controlled "#define" diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -17,19 +17,18 @@ _r_comment = re.compile(r"/\*.*?\*/|//([^\n\\]|\\.)*?$", re.DOTALL | re.MULTILINE) -_r_define = re.compile(r"^[ \t\r\f\v]*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) _r_partial_enum = re.compile(r"=\s*\.\.\.\s*[,}]|\.\.\.\s*\}") _r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$") _r_partial_array = re.compile(r"\[\s*\.\.\.\s*\]") _r_words = re.compile(r"\w+|\S") _parser_cache = None _r_int_literal = re.compile(r"-?0?x?[0-9a-f]+[lu]*$", re.IGNORECASE) -_r_ifdef = re.compile(r"^[ \t\r\f\v]*#\s*(if|elif|ifdef|ifndef|else|endif)" - r"\b((?:[^\n\\]|\\.)*?)$", - re.DOTALL | re.MULTILINE) +_r_ifdef = re.compile( + r"^[ \t\r\f\v]*#\s*(if|elif|ifdef|ifndef|else|endif|define)" + r"\b((?:[^\n\\]|\\.)*?)$", + re.DOTALL | re.MULTILINE) _r_multispaces = re.compile(r"\s+", re.MULTILINE) +_r_single_word = re.compile(r"[A-Za-z_][A-Za-z0-9_]+") def _get_parser(): global _parser_cache @@ -41,13 +40,6 @@ # Remove comments. NOTE: this only work because the cdef() section # should not contain any string literal! csource = _r_comment.sub(' ', csource) - # Remove the "#define FOO x" lines - macros = {} - for match in _r_define.finditer(csource): - macroname, macrovalue = match.groups() - macrovalue = macrovalue.replace('\\\n', '').strip() - macros[macroname] = macrovalue - csource = _r_define.sub('', csource) # Replace "[...]" with "[__dotdotdotarray__]" csource = _r_partial_array.sub('[__dotdotdotarray__]', csource) # Replace "...}" with "__dotdotdotNUM__}". This construction should @@ -69,7 +61,7 @@ csource[p+3:]) # Replace all remaining "..." with the same name, "__dotdotdot__", # which is declared with a typedef for the purpose of C parsing. - return csource.replace('...', ' __dotdotdot__ '), macros + return csource.replace('...', ' __dotdotdot__ ') def _common_type_names(csource): # Look in the source for what looks like usages of types from the @@ -133,6 +125,7 @@ result = ' && '.join(['(%s)' % s for s in stack]) result = result.replace('\x00', ' && ') ifdefs.extend([result] * (linenum1 - n)) + return result def pop(): if not stack: @@ -148,11 +141,19 @@ for match in _r_ifdef.finditer(csource): linenum1 = csource[:match.start()].count('\n') linenum2 = linenum1 + match.group().count('\n') + 1 - flush() + current_outer_condition = flush() keyword = match.group(1) condition = match.group(2).replace('\\\n', '').strip() condition = _r_multispaces.sub(' ', condition) - if keyword == 'if': + if keyword == 'define': + match2 = _r_single_word.match(condition) + if not match2: + raise api.CDefError("line %d: '#define' not followed by " + "a word" % linenum1) + singleword = match2.group() + defines.append((current_outer_condition, singleword, + condition[len(singleword):].strip())) + elif keyword == 'if': stack.append(condition) elif keyword == 'ifdef': stack.append('defined(%s)' % condition) @@ -181,10 +182,10 @@ num_eol = m.group().count('\n') return num_eol * '\n' csource = _r_ifdef.sub(replace_with_eol, csource) - return csource, ifdefs + return csource, defines, ifdefs def _parse(self, csource): - csource, defines = _preprocess(csource) + csource = _preprocess(csource) # XXX: for more efficiency we would need to poke into the # internals of CParser... the following registers the @@ -204,7 +205,7 @@ csourcelines.append(csource) csource = '\n'.join(csourcelines) - csource, ifdefs = self._extract_ifdefs(csource) + csource, defines, ifdefs = self._extract_ifdefs(csource) if lock is not None: lock.acquire() # pycparser is not thread-safe... @@ -381,8 +382,9 @@ self._declare(('variable', decl.name, ifdef), tp) def parse_type(self, cdecl): - ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] + ast, macros, _, ifdefs = self._parse('void __dummy(\n%s\n);' % cdecl) assert not macros + assert not ifdefs exprnode = ast.ext[-1].type.args.params[0] if isinstance(exprnode, pycparser.c_ast.ID): raise api.CDefError("unknown identifier '%s'" % (exprnode.name,)) diff --git a/testing/cffi0/test_parsing.py b/testing/cffi0/test_parsing.py --- a/testing/cffi0/test_parsing.py +++ b/testing/cffi0/test_parsing.py @@ -152,7 +152,7 @@ def test_extract_ifdefs_1(): parser = Parser() - _, macros = parser._extract_ifdefs(""" + _, _, macros = parser._extract_ifdefs(""" #ifdef FOO int q; #endif @@ -175,7 +175,7 @@ def test_extract_ifdefs_2(): parser = Parser() - _, macros = parser._extract_ifdefs(""" + _, _, macros = parser._extract_ifdefs(""" #if FOO int q; #else @@ -204,7 +204,7 @@ def test_extract_ifdefs_3(): parser = Parser() - _, macros = parser._extract_ifdefs(""" + _, _, macros = parser._extract_ifdefs(""" #if FOO int q; #elif BAR @@ -233,7 +233,7 @@ def test_extract_ifdefs_4(): parser = Parser() - _, macros = parser._extract_ifdefs(""" + _, _, macros = parser._extract_ifdefs(""" #ifdef ABC #ifdef BCD int q; @@ -264,7 +264,7 @@ def test_extract_ifdefs_continuation(): parser = Parser() - clean, macros = parser._extract_ifdefs(r""" // <= note the 'r' here + clean, _, macros = parser._extract_ifdefs(r""" // <= note the 'r' here #if FOO \ FO\\O2 int q; @@ -297,7 +297,7 @@ def test_clean_ifdefs(): parser = Parser() - clean, _ = parser._extract_ifdefs(""" + clean, _, _ = parser._extract_ifdefs(""" #if FOO int q; #else @@ -321,6 +321,28 @@ int z; """ +def test_defines_with_ifdefs(): + parser = Parser() + _, defines, macros = parser._extract_ifdefs(""" + #if FOO + # define ABC 42 + #else + # define BCD 4\\ +3 + #endif + """) + + assert macros == [ + '', + None, + None, + None, + None, + None, + None, + ''] + assert defines == [('FOO', 'ABC', '42'), ('!(FOO)', 'BCD', '43')] + def test_remove_comments(): ffi = FFI(backend=FakeBackend()) ffi.cdef(""" @@ -362,10 +384,13 @@ 42 #define BCD \\ 43 + #define CDE 3\\ +9 """) m = ffi.dlopen(lib_m) assert m.ABC == 42 assert m.BCD == 43 + assert m.CDE == 39 def test_define_not_supported_for_now(): ffi = FFI(backend=FakeBackend()) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit