[pypy-commit] cffi cmacros: Reintroduce condition-controlled "#define"
arigo
noreply at buildbot.pypy.org
Thu Jul 30 22:41:30 CEST 2015
Author: Armin Rigo <arigo at 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())
More information about the pypy-commit
mailing list