summaryrefslogtreecommitdiff
path: root/giscanner/transformer.py
diff options
context:
space:
mode:
Diffstat (limited to 'giscanner/transformer.py')
-rw-r--r--giscanner/transformer.py390
1 files changed, 72 insertions, 318 deletions
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 0ed7ca00..7493a353 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -19,14 +19,12 @@
#
import os
-import re
from .ast import (Callback, Enum, Function, Namespace, Member,
- Parameter, Return, Array, Struct, Field,
- Type, Alias, Interface, Class, Node, Union,
- List, Map, Varargs, Constant, type_name_from_ctype,
- type_names, default_array_types, default_out_types,
- TYPE_STRING, BASIC_GIR_TYPES, TYPE_NONE)
+ Parameter, Return, Struct, Field,
+ Type, Array, Alias, Interface, Class, Node, Union,
+ Varargs, Constant, type_name_from_ctype,
+ type_names, TYPE_STRING, BASIC_GIR_TYPES)
from .config import DATADIR
from .glibast import GLibBoxed
from .girparser import GIRParser
@@ -75,8 +73,6 @@ class Transformer(object):
self._strip_prefix = ''
self._includes = set()
self._includepaths = []
- self._list_ctypes = []
- self._map_ctypes = []
def get_names(self):
return self._names
@@ -84,10 +80,6 @@ class Transformer(object):
def get_includes(self):
return self._includes
- def set_container_types(self, list_ctypes, map_ctypes):
- self._list_ctypes = list_ctypes
- self._map_ctypes = map_ctypes
-
def set_strip_prefix(self, strip_prefix):
self._strip_prefix = strip_prefix
@@ -109,6 +101,7 @@ class Transformer(object):
self._includes.add(include)
# Private
+
def _find_include(self, include):
searchdirs = self._includepaths[:]
for path in _xdg_data_dirs:
@@ -210,7 +203,6 @@ class Transformer(object):
def _create_enum(self, symbol):
members = []
- directives = symbol.directives()
for child in symbol.base_type.child_list:
name = strip_common_prefix(symbol.ident, child.ident).lower()
members.append(Member(name,
@@ -219,7 +211,6 @@ class Transformer(object):
enum_name = self.remove_prefix(symbol.ident)
enum = Enum(enum_name, symbol.ident, members)
- self._parse_version(enum, directives)
self._names.type_names[symbol.ident] = (None, enum)
return enum
@@ -227,48 +218,6 @@ class Transformer(object):
return Member(symbol.ident, symbol.base_type.name,
symbol.ident)
- def _parse_deprecated(self, node, directives):
- deprecated = directives.get('deprecated', False)
- if deprecated:
- deprecated_value = deprecated[0]
- if ':' in deprecated_value:
- # Split out gtk-doc version
- (node.deprecated_version, node.deprecated) = \
- [x.strip() for x in deprecated_value.split(':', 1)]
- else:
- # No version, just include str
- node.deprecated = deprecated_value.strip()
-
- def _parse_version(self, node, directives):
- version = directives.get('since', False)
- if version:
- version_value = version[0]
- node.version = version_value.strip()
-
- def _pair_array(self, params, array):
- if not array.type.length_param_name:
- return
- target_name = array.type.length_param_name
- for i, param in enumerate(params):
- if param.name == array.type.length_param_name:
- array.type.length_param_index = i
- return
- raise ValueError("Unmatched length parameter name %r"\
- % (target_name, ))
-
- def _pair_annotations(self, params, return_):
- names = {}
- for param in params:
- if param.name in names:
- raise ValueError("Duplicate parameter name %r"\
- % (param.name, ))
- names[param.name] = 1
- if isinstance(param.type, Array):
- self._pair_array(params, param)
-
- if isinstance(return_.type, Array):
- self._pair_array(params, return_)
-
def _type_is_callback(self, type):
if (isinstance(type, Callback) or
isinstance(self._typedefs_ns.get(type.name), Callback)):
@@ -294,50 +243,35 @@ class Transformer(object):
def _augment_callback_params(self, params):
for i, param in enumerate(params):
- if self._type_is_callback(param.type):
- # j is the index where we look for closure/destroy to
- # group with the callback param
- j = i + 1
- if j == len(params):
- continue # no more args -> nothing to group look
- # at the param directly following for either a closure
- # or a destroy; only one of these will fire
- had_closure = self._handle_closure(param, j, params[j])
- had_destroy = self._handle_destroy(param, j, params[j])
- j += 1
- # are we out of params, or did we find neither?
- if j == len(params) or (not had_closure and not had_destroy):
- continue
- # we found either a closure or a destroy; check the
- # parameter following for the other
- if not had_closure:
- self._handle_closure(param, j, params[j])
- if not had_destroy:
- self._handle_destroy(param, j, params[j])
-
- # We take the annotations from the parser as strings; here we
- # want to split them into components, so:
- # (transfer full) -> {'transfer' : [ 'full' ]}
- def _parse_options(self, options):
- ret = {}
- ws_re = re.compile(r'\s+')
- for opt in options:
- items = ws_re.split(opt)
- ret[items[0]] = items[1:]
- return ret
+ if not self._type_is_callback(param.type):
+ continue
+
+ # j is the index where we look for closure/destroy to
+ # group with the callback param
+ j = i + 1
+ if j == len(params):
+ continue # no more args -> nothing to group look
+ # at the param directly following for either a closure
+ # or a destroy; only one of these will fire
+ had_closure = self._handle_closure(param, j, params[j])
+ had_destroy = self._handle_destroy(param, j, params[j])
+ j += 1
+ # are we out of params, or did we find neither?
+ if j == len(params) or (not had_closure and not had_destroy):
+ continue
+ # we found either a closure or a destroy; check the
+ # parameter following for the other
+ if not had_closure:
+ self._handle_closure(param, j, params[j])
+ if not had_destroy:
+ self._handle_destroy(param, j, params[j])
def _create_function(self, symbol):
- directives = symbol.directives()
- parameters = list(self._create_parameters(
- symbol.base_type, directives))
- return_ = self._create_return(symbol.base_type.base_type,
- directives.get('return', {}))
+ parameters = list(self._create_parameters(symbol.base_type))
+ return_ = self._create_return(symbol.base_type.base_type)
self._augment_callback_params(parameters)
- self._pair_annotations(parameters, return_)
name = self._strip_namespace_func(symbol.ident)
func = Function(name, return_, parameters, symbol.ident)
- self._parse_version(func, directives)
- self._parse_deprecated(func, directives)
return func
def _create_source_type(self, source_type):
@@ -357,47 +291,42 @@ class Transformer(object):
value = 'any'
return value
- def _create_parameters(self, base_type, directives=None):
- if directives is None:
- dirs = {}
- else:
- dirs = directives
+ def _create_parameters(self, base_type):
# warn if we see annotations for unknown parameters
param_names = set(child.ident for child in base_type.child_list)
- dirs_for = set(dirs)
- dirs_for = dirs_for.difference(param_names)
- dirs_for.discard('return')
- dirs_for.discard('deprecated')
- dirs_for.discard('since')
- if dirs_for:
- print 'Unexpected annotations for %s, parameters are %s' % (
- list(dirs_for), list(param_names), )
-
for child in base_type.child_list:
- yield self._create_parameter(
- child, dirs.get(child.ident, {}))
+ yield self._create_parameter(child)
def _create_member(self, symbol):
- ctype = symbol.base_type.type
- if (ctype == CTYPE_POINTER and
+ source_type = symbol.base_type
+ if (source_type.type == CTYPE_POINTER and
symbol.base_type.base_type.type == CTYPE_FUNCTION):
node = self._create_callback(symbol)
else:
- opts = {}
- if ctype == CTYPE_ARRAY:
- opts['array'] = []
+ # Special handling for fields; we don't have annotations on them
+ # to apply later, yet.
+ if source_type.type == CTYPE_ARRAY:
+ ctype = self._create_source_type(source_type)
+ canonical_ctype = self._canonicalize_ctype(ctype)
+ if canonical_ctype[-1] == '*':
+ derefed_name = canonical_ctype[:-1]
+ else:
+ derefed_name = canonical_ctype
+ derefed_name = self.resolve_param_type(derefed_name)
+ ftype = Array(ctype, self.parse_ctype(derefed_name))
child_list = list(symbol.base_type.child_list)
+ ftype.zeroterminated = False
if child_list:
- size_opt = 'fixed-size=%d' % (child_list[0].const_int, )
- opts['array'].append(size_opt)
- ftype = self._create_type(symbol.base_type, opts,
- is_param=False, is_retval=False)
+ ftype.size = '%d' % (child_list[0].const_int, )
+ else:
+ ftype = self._create_type(symbol.base_type,
+ is_param=False, is_retval=False)
ftype = self.resolve_param_type(ftype)
# Fields are assumed to be read-write
# (except for Objects, see also glibtransformer.py)
- node = Field(symbol.ident, ftype, symbol.ident,
- readable=True, writable=True, bits=symbol.const_int)
+ node = Field(symbol.ident, ftype, ftype.name,
+ readable=True, writable=True, bits=symbol.const_int)
return node
def _create_typedef(self, symbol):
@@ -473,7 +402,7 @@ class Transformer(object):
else:
return derefed_typename
- def _create_type(self, source_type, options, is_param, is_retval):
+ def _create_type(self, source_type, is_param, is_retval):
ctype = self._create_source_type(source_type)
if ctype == 'va_list':
raise SkipError()
@@ -482,188 +411,37 @@ class Transformer(object):
elif ctype == 'FILE*':
raise SkipError
- canonical_ctype = self._canonicalize_ctype(ctype)
-
- # Now check for a list/map/array type
- if canonical_ctype in self._list_ctypes:
- param = options.get('element-type')
- if param:
- contained_type = self.parse_ctype(param[0])
- else:
- contained_type = None
- derefed_name = self.parse_ctype(ctype)
- rettype = List(derefed_name,
- ctype,
- contained_type)
- elif canonical_ctype in self._map_ctypes:
- param = options.get('element-type')
- if param:
- key_type = self.parse_ctype(param[0])
- value_type = self.parse_ctype(param[1])
- else:
- key_type = None
- value_type = None
- derefed_name = self.parse_ctype(ctype)
- rettype = Map(derefed_name,
- ctype,
- key_type, value_type)
- elif ((is_param and canonical_ctype in default_array_types
- and not 'out' in options)
- or ('array' in options)):
- if canonical_ctype[-1] == '*':
- derefed_name = canonical_ctype[:-1]
- else:
- derefed_name = canonical_ctype
- rettype = Array(ctype,
- self.parse_ctype(derefed_name))
- array_opts = dict([opt.split('=')
- for opt in options.get('array', [])])
- if 'length' in array_opts:
- rettype.length_param_name = array_opts['length']
- rettype.zeroterminated = False
- if 'fixed-size' in array_opts:
- rettype.size = array_opts['fixed-size']
- rettype.zeroterminated = False
- if 'zero-terminated' in array_opts:
- rettype.zeroterminated = array_opts['zero-terminated'] != '0'
- else:
- derefed_name = self.parse_ctype(ctype,
- not (is_param or is_retval))
- rettype = Type(derefed_name, ctype)
-
- # Deduce direction for some types passed by reference that
- # aren't arrays; modifies the options array.
- if ('array' not in options and
- not ('out' in options or
- 'in' in options or
- 'inout' in options or
- 'in-out' in options) and
- source_type.type == CTYPE_POINTER and
- derefed_name in default_out_types):
- options['out'] = []
-
- if 'transfer' in options:
- # Transfer is specified, we don't question it.
- return rettype
+ is_member = not (is_param or is_retval)
+ # Here we handle basic type parsing; most of the heavy lifting
+ # and inference comes in annotationparser.py when we merge
+ # in annotation data.
+ derefed_name = self.parse_ctype(ctype, is_member)
+ rettype = Type(derefed_name, ctype)
+ rettype.canonical = self._canonicalize_ctype(ctype)
+ derefed_ctype = ctype.replace('*', '')
+ rettype.derefed_canonical = self._canonicalize_ctype(derefed_ctype)
canontype = type_name_from_ctype(ctype)
-
- # Since no transfer is specified, we drop into a bunch of
- # heuristics to guess it. This mutates the options array to
- # set the 'transfer' option.
- # Note that we inferred the transfer
- options['transfer-inferred'] = []
- stype = source_type
- if canontype == TYPE_STRING:
- # It's a string - we just look at 'const'
- if source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST:
- options['transfer'] = ['none']
- else:
- options['transfer'] = ['full']
- elif 'array' in options or stype.type == CTYPE_ARRAY:
- # It's rare to mutate arrays in public GObject APIs
- options['transfer'] = ['none']
- elif (canontype in BASIC_GIR_TYPES or
- canontype == TYPE_NONE or
- stype.type == CTYPE_ENUM):
- # Basic types default to 'none'
- options['transfer'] = ['none']
- elif (stype.type == CTYPE_POINTER and
- stype.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
- # Anything with 'const' gets none
- options['transfer'] = ['none']
- elif is_param and stype.type == CTYPE_POINTER:
- # For generic pointer types, let's look at the argument
- # direction. An out/inout argument gets full, everything
- # else none.
- if ('out' in options or
- 'inout' in options or
- 'in-out' in options):
- options['transfer'] = ['full']
- else:
- options['transfer'] = ['none']
- else:
- # For anything else we default to none for parameters;
- # this covers enums and possibly some other corner cases.
- # Return values of structures and the like will end up
- # full.
- if is_param:
- options['transfer'] = ['none']
- else:
- options['transfer'] = ['full']
-
+ if ((canontype == TYPE_STRING or
+ source_type.type == CTYPE_POINTER) and
+ source_type.base_type.type_qualifier & TYPE_QUALIFIER_CONST):
+ rettype.is_const = True
return rettype
- def _handle_generic_param_options(self, param, options):
- for option, data in options.iteritems():
- if option == 'transfer':
- if data:
- depth = data[0]
- if depth not in ('none', 'container', 'full'):
- raise ValueError("Invalid transfer %r" % (depth, ))
- else:
- depth = 'full'
- param.transfer = depth
- elif option == 'transfer-inferred':
- # This is a purely internal flag; we don't expect
- # people to write it
- param.transfer_inferred = True
-
- def _create_parameter(self, symbol, options):
- options = self._parse_options(options)
+ def _create_parameter(self, symbol):
if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
ptype = Varargs()
- if 'transfer' not in options:
- options['transfer'] = ['none']
else:
- ptype = self._create_type(symbol.base_type, options,
+ ptype = self._create_type(symbol.base_type,
is_param=True, is_retval=False)
ptype = self.resolve_param_type(ptype)
- param = Parameter(symbol.ident, ptype)
- for option, data in options.iteritems():
- if option in ['in-out', 'inout']:
- param.direction = 'inout'
- elif option == 'in':
- param.direction = 'in'
- elif option == 'out':
- param.direction = 'out'
- elif option == 'allow-none':
- param.allow_none = True
- elif option.startswith(('element-type', 'array')):
- pass
- elif option in ('transfer', 'transfer-inferred'):
- pass
- elif option == 'scope':
- param.scope = data[0]
- else:
- print 'Unhandled parameter annotation option: %r' % (
- option, )
- self._handle_generic_param_options(param, options)
+ return Parameter(symbol.ident, ptype)
- assert param.transfer is not None, param
- return param
-
- def _create_return(self, source_type, options=None):
- if options is None:
- options_map = {}
- else:
- options_map = self._parse_options(options)
- rtype = self._create_type(source_type, options_map,
+ def _create_return(self, source_type):
+ rtype = self._create_type(source_type,
is_param=False, is_retval=True)
rtype = self.resolve_param_type(rtype)
return_ = Return(rtype)
- self._handle_generic_param_options(return_, options_map)
- for option, data in options_map.iteritems():
- if option in ('transfer', 'transfer-inferred',
- 'element-type', 'out'):
- pass
- elif option.startswith(('element-type', 'array')):
- pass
- else:
- print 'Unhandled return type annotation option: %r' % (
- option, )
-
- assert return_.transfer is not None, return_
return return_
def _create_const(self, symbol):
@@ -697,7 +475,6 @@ class Transformer(object):
return callback
def _create_struct(self, symbol):
- directives = symbol.directives()
struct = self._typedefs_ns.get(symbol.ident, None)
if struct is None:
# This is a bit of a hack; really we should try
@@ -715,12 +492,9 @@ class Transformer(object):
if field:
struct.fields.append(field)
- self._parse_version(struct, directives)
-
return struct
def _create_union(self, symbol):
- directives = symbol.directives()
union = self._typedefs_ns.get(symbol.ident, None)
if union is None:
# This is a bit of a hack; really we should try
@@ -738,24 +512,17 @@ class Transformer(object):
if field:
union.fields.append(field)
- self._parse_version(union, directives)
-
return union
def _create_callback(self, symbol):
- directives = symbol.directives()
- parameters = self._create_parameters(symbol.base_type.base_type,
- directives)
- retval = self._create_return(symbol.base_type.base_type.base_type,
- directives.get('return', {}))
+ parameters = self._create_parameters(symbol.base_type.base_type)
+ retval = self._create_return(symbol.base_type.base_type.base_type)
if symbol.ident.find('_') > 0:
name = self.remove_prefix(symbol.ident, True)
else:
name = self.remove_prefix(symbol.ident)
callback = Callback(name, retval, list(parameters), symbol.ident)
- self._parse_version(callback, directives)
-
return callback
def _typepair_to_str(self, item):
@@ -833,19 +600,6 @@ class Transformer(object):
ptype.name = self.resolve_type_name_full(ptype.name,
self.ctype_of(ptype),
names, **kwargs)
- if isinstance(ptype, (Array, List)):
- if ptype.element_type is not None:
- ptype.element_type = \
- self.resolve_param_type_full(ptype.element_type,
- names, **kwargs)
- if isinstance(ptype, Map):
- if ptype.key_type is not None:
- ptype.key_type = \
- self.resolve_param_type_full(ptype.key_type,
- names, **kwargs)
- ptype.value_type = \
- self.resolve_param_type_full(ptype.value_type,
- names, **kwargs)
elif isinstance(ptype, basestring):
return self.resolve_type_name_full(ptype, None, names, **kwargs)
else: