Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r2365:97315bc352f8 Date: 2015-11-02 22:22 +0100 http://bitbucket.org/cffi/cffi/changeset/97315bc352f8/
Log: Found a pycparser issue. Work around it by adding some parentheses in the csource passed to it, to avoid the buggy cases. diff --git a/cffi/cparser.py b/cffi/cparser.py --- a/cffi/cparser.py +++ b/cffi/cparser.py @@ -29,6 +29,8 @@ _r_stdcall1 = re.compile(r"\b(__stdcall|WINAPI)\b") _r_stdcall2 = re.compile(r"[(]\s*(__stdcall|WINAPI)\b") _r_cdecl = re.compile(r"\b__cdecl\b") +_r_star_const_space = re.compile( # matches "* const " + r"[*]\s*((const|volatile|restrict)\b\s*)+") def _get_parser(): global _parser_cache @@ -47,6 +49,45 @@ macrovalue = macrovalue.replace('\\\n', '').strip() macros[macroname] = macrovalue csource = _r_define.sub('', csource) + # + # Workaround for a pycparser issue: "char*const***" gives us a wrong + # syntax tree, the same as for "char***(*const)". This means we can't + # tell the difference afterwards. But "char(*const(***))" gives us + # the right syntax tree. The issue only occurs if there are several + # stars in sequence with no parenthesis inbetween, just possibly + # qualifiers. Attempt to fix it by adding some parentheses in the + # source: each time we see "* const" or "* const *", we add an opening + # parenthesis before each star---the hard part is figuring out where + # to close them. + parts = [] + while True: + match = _r_star_const_space.search(csource) + if not match: + break + #print repr(''.join(parts)+csource), '=>', + parts.append(csource[:match.start()]) + parts.append('('); closing = ')' + parts.append(match.group()) # e.g. "* const " + endpos = match.end() + if csource.startswith('*', endpos): + parts.append('('); closing += ')' + level = 0 + for i in xrange(endpos, len(csource)): + c = csource[i] + if c == '(': + level += 1 + elif c == ')': + if level == 0: + break + level -= 1 + elif c in ',;=': + if level == 0: + break + csource = csource[endpos:i] + closing + csource[i:] + #print repr(''.join(parts)+csource) + parts.append(csource) + csource = ''.join(parts) + # # BIG HACK: replace WINAPI or __stdcall with "volatile const". # It doesn't make sense for the return type of a function to be # "volatile volatile const", so we abuse it to detect __stdcall... @@ -320,13 +361,15 @@ self._declare('variable ' + decl.name, tp, quals=quals) def parse_type(self, cdecl): + return self.parse_type_and_quals(cdecl)[0] + + def parse_type_and_quals(self, cdecl): ast, macros = self._parse('void __dummy(\n%s\n);' % cdecl)[:2] assert not macros exprnode = ast.ext[-1].type.args.params[0] if isinstance(exprnode, pycparser.c_ast.ID): raise api.CDefError("unknown identifier '%s'" % (exprnode.name,)) - tp, quals = self._get_type_and_quals(exprnode.type) - return tp + return self._get_type_and_quals(exprnode.type) def _declare(self, name, obj, included=False, quals=0): if name in self._declarations: 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 @@ -367,6 +367,41 @@ assert lst[0] == lst[2] assert lst[1] == lst[3] +def test_const_pointer_to_pointer(): + from cffi import model + ffi = FFI(backend=FakeBackend()) + # + tp, qual = ffi._parser.parse_type_and_quals("char * * (* const)") + assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) + tp, qual = ffi._parser.parse_type_and_quals("char * (* const (*))") + assert (str(tp), qual) == ("<char * * const *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("char (* const (* (*)))") + assert (str(tp), qual) == ("<char * const * *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("char const * * *") + assert (str(tp), qual) == ("<char const * * *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("const char * * *") + assert (str(tp), qual) == ("<char const * * *>", 0) + # + tp, qual = ffi._parser.parse_type_and_quals("char * * * const const") + assert (str(tp), qual) == ("<char * * *>", model.Q_CONST) + tp, qual = ffi._parser.parse_type_and_quals("char * * volatile *") + assert (str(tp), qual) == ("<char * * volatile *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("char * volatile restrict * *") + assert (str(tp), qual) == ("<char * __restrict volatile * *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("char const volatile * * *") + assert (str(tp), qual) == ("<char volatile const * * *>", 0) + tp, qual = ffi._parser.parse_type_and_quals("const char * * *") + assert (str(tp), qual) == ("<char const * * *>", 0) + # + tp, qual = ffi._parser.parse_type_and_quals( + "int(char*const*, short****const*)") + assert (str(tp), qual) == ( + "<int()(char * const *, short * * * * const *)>", 0) + tp, qual = ffi._parser.parse_type_and_quals( + "char*const*(short*const****)") + assert (str(tp), qual) == ( + "<char * const *()(short * const * * * *)>", 0) + def test_enum(): ffi = FFI() ffi.cdef(""" diff --git a/testing/cffi1/test_verify1.py b/testing/cffi1/test_verify1.py --- a/testing/cffi1/test_verify1.py +++ b/testing/cffi1/test_verify1.py @@ -2250,3 +2250,8 @@ assert p == lib.myarray + 4 p[1] = 82 assert lib.my_value == 82 # [5] + +def test_const_pointer_to_pointer(): + ffi = FFI() + ffi.cdef("struct s { char *const *a; };") + ffi.verify("struct s { char *const *a; };") _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit