[pypy-commit] cffi default: Add support for constants, either via "static sometype const x; "
arigo
noreply at buildbot.pypy.org
Fri Jun 15 19:11:55 CEST 2012
Author: Armin Rigo <arigo at tunes.org>
Branch:
Changeset: r369:02aa72e88cf9
Date: 2012-06-15 19:11 +0200
http://bitbucket.org/cffi/cffi/changeset/02aa72e88cf9/
Log: Add support for constants, either via "static sometype const x;" or
via "#define X ...".
diff --git a/cffi/cparser.py b/cffi/cparser.py
--- a/cffi/cparser.py
+++ b/cffi/cparser.py
@@ -3,6 +3,8 @@
import pycparser, weakref, re
_r_comment = re.compile(r"/\*.*?\*/|//.*?$", re.DOTALL | re.MULTILINE)
+_r_define = re.compile(r"^\s*#\s*define\s+([A-Za-z_][A-Za-z_0-9]*)\s+(.*?)$",
+ re.MULTILINE)
_r_partial_enum = re.compile(r"\.\.\.\s*\}")
_r_enum_dotdotdot = re.compile(r"__dotdotdot\d+__$")
_parser_cache = None
@@ -17,6 +19,12 @@
# 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()
+ macros[macroname] = macrovalue
+ csource = _r_define.sub('', csource)
# Replace "...}" with "__dotdotdotNUM__}". This construction should
# occur only at the end of enums; at the end of structs we have "...;}"
# and at the end of vararg functions "...);"
@@ -28,7 +36,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__ ')
+ return csource.replace('...', ' __dotdotdot__ '), macros
class Parser(object):
def __init__(self):
@@ -46,13 +54,21 @@
if name.startswith('typedef '):
csourcelines.append('typedef int %s;' % (name[8:],))
csourcelines.append('typedef int __dotdotdot__;')
- csourcelines.append(_preprocess(csource))
+ csource, macros = _preprocess(csource)
+ csourcelines.append(csource)
csource = '\n'.join(csourcelines)
ast = _get_parser().parse(csource)
- return ast
+ return ast, macros
def parse(self, csource):
- ast = self._parse(csource)
+ ast, macros = self._parse(csource)
+ # add the macros
+ for key, value in macros.items():
+ value = value.strip()
+ if value != '...':
+ raise api.CDefError('only supports the syntax "#define '
+ '%s ..." for now (literally)' % key)
+ self._declare('macro ' + key, value)
# find the first "__dotdotdot__" and use that as a separator
# between the repeated typedefs and the real csource
iterator = iter(ast.ext)
@@ -98,13 +114,14 @@
#
if decl.name:
tp = self._get_type(node)
- if 'const' in decl.quals:
+ if self._is_constant_declaration(node):
self._declare('constant ' + decl.name, tp)
else:
self._declare('variable ' + decl.name, tp)
def parse_type(self, cdecl, force_pointer=False):
- ast = self._parse('void __dummy(%s);' % cdecl)
+ ast, macros = self._parse('void __dummy(%s);' % cdecl)
+ assert not macros
typenode = ast.ext[-1].type.args.params[0].type
return self._get_type(typenode, force_pointer=force_pointer)
@@ -155,7 +172,6 @@
const = (isinstance(typenode.type, pycparser.c_ast.TypeDecl)
and 'const' in typenode.type.quals)
return self._get_type_pointer(self._get_type(typenode.type), const)
-
#
if isinstance(typenode, pycparser.c_ast.TypeDecl):
type = typenode.type
@@ -216,6 +232,16 @@
result = self._get_type(typenode.type)
return model.FunctionType(tuple(args), result, ellipsis)
+ def _is_constant_declaration(self, typenode, const=False):
+ if isinstance(typenode, pycparser.c_ast.ArrayDecl):
+ return self._is_constant_declaration(typenode.type)
+ if isinstance(typenode, pycparser.c_ast.PtrDecl):
+ const = 'const' in typenode.quals
+ return self._is_constant_declaration(typenode.type, const)
+ if isinstance(typenode, pycparser.c_ast.TypeDecl):
+ return const or 'const' in typenode.quals
+ return False
+
def _get_struct_or_union_type(self, kind, type, typenode=None):
# First, a level of caching on the exact 'type' node of the AST.
# This is obscure, but needed because pycparser "unrolls" declarations
diff --git a/cffi/verifier.py b/cffi/verifier.py
--- a/cffi/verifier.py
+++ b/cffi/verifier.py
@@ -400,6 +400,17 @@
tp.partial = False
# ----------
+ # macros: for now only for integers
+
+ def generate_cpy_macro_decl(self, tp, name):
+ assert tp == '...'
+ self._generate_cpy_const(True, name)
+
+ generate_cpy_macro_method = generate_nothing
+ loading_cpy_macro = loaded_noop
+ loaded_cpy_macro = loaded_noop
+
+ # ----------
cffimod_header = r'''
#include <Python.h>
diff --git a/doc/source/index.rst b/doc/source/index.rst
--- a/doc/source/index.rst
+++ b/doc/source/index.rst
@@ -311,6 +311,16 @@
with structs, an ``enum`` that does not end in ``...`` is assumed to
be exact, and this is checked.
+* integer macros: you can write in the ``cdef`` the line
+ "``#define FOO ...``", with any macro name FOO. Provided the macro
+ is defined to be an integer value, this value will be available via
+ an attribute of the library object returned by ``verify()``. The
+ same effect can be achieved by writing a declaration
+ ``static const int FOO;``. The latter is more general because it
+ supports other types than integer types (note: the syntax is then
+ to write the ``const`` together with the variable name, as in
+ ``static char *const FOO;``).
+
Working with pointers, structures and arrays
--------------------------------------------
diff --git a/testing/backend_tests.py b/testing/backend_tests.py
--- a/testing/backend_tests.py
+++ b/testing/backend_tests.py
@@ -786,7 +786,7 @@
def test_new_struct_containing_array_varsize(self):
py.test.skip("later?")
- ffi = FFI(backend=_ffi_backend)
+ ffi = FFI(backend=self.Backend())
ffi.cdef("struct foo_s { int len; short data[]; };")
p = ffi.new("struct foo_s", 10) # a single integer is the length
assert p.len == 0
diff --git a/testing/test_parsing.py b/testing/test_parsing.py
--- a/testing/test_parsing.py
+++ b/testing/test_parsing.py
@@ -1,4 +1,5 @@
-from cffi import FFI
+import py
+from cffi import FFI, CDefError
class FakeBackend(object):
@@ -139,3 +140,9 @@
func = m.sin
assert func.name == 'sin'
assert func.BType == '<func (<double>, <double>), <double>, False>'
+
+def test_define_not_supported_for_now():
+ ffi = FFI(backend=FakeBackend())
+ e = py.test.raises(CDefError, ffi.cdef, "#define FOO 42")
+ assert str(e.value) == \
+ 'only supports the syntax "#define FOO ..." for now (literally)'
diff --git a/testing/test_verify.py b/testing/test_verify.py
--- a/testing/test_verify.py
+++ b/testing/test_verify.py
@@ -307,6 +307,13 @@
assert lib.AA == value
assert type(lib.AA) is type(int(lib.AA))
+def test_global_constants_non_int():
+ ffi = FFI()
+ ffi.cdef("static char *const PP;")
+ lib = ffi.verify('static char *const PP = "testing!";\n')
+ assert ffi.typeof(lib.PP) == ffi.typeof("char *")
+ assert str(lib.PP) == "testing!"
+
def test_nonfull_enum():
ffi = FFI()
ffi.cdef("enum ee { EE1, EE2, EE3, ... \n \t };")
@@ -340,3 +347,12 @@
ffi.errno = 15
assert lib.foo(6) == 42
assert ffi.errno == 16
+
+def test_define_int():
+ ffi = FFI()
+ ffi.cdef("#define FOO ...\n"
+ "#define BAR ...")
+ lib = ffi.verify("#define FOO 42\n"
+ "#define BAR (-44)\n")
+ assert lib.FOO == 42
+ assert lib.BAR == -44
More information about the pypy-commit
mailing list