diff options
author | Dieter Verfaillie <dieterv@optionexplicit.be> | 2013-07-25 17:25:49 +0200 |
---|---|---|
committer | Dieter Verfaillie <dieterv@optionexplicit.be> | 2013-10-08 20:55:53 +0200 |
commit | 4ff3c660de64ba423659bd796fbd944b7af1913d (patch) | |
tree | c7eb4ae8ba9a6c39576052cbb459a0a2322e7031 /giscanner/annotationparser.py | |
parent | b6954536f32352c0c29fe5a9a73de1018559a9c5 (diff) | |
download | gobject-introspection-4ff3c660de64ba423659bd796fbd944b7af1913d.tar.gz |
giscanner: give parameters their own storage class
Diffstat (limited to 'giscanner/annotationparser.py')
-rw-r--r-- | giscanner/annotationparser.py | 261 |
1 files changed, 221 insertions, 40 deletions
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py index 8351365e..ce1f7236 100644 --- a/giscanner/annotationparser.py +++ b/giscanner/annotationparser.py @@ -490,16 +490,202 @@ class GtkDocAnnotations(OrderedDict): self.position = position -class GtkDocTag(object): +class GtkDocParameter(object): + ''' + Represents a GTK-Doc parameter part. + ''' - __slots__ = ('name', 'annotations', 'description', 'value', 'position') + __slots__ = ('name', 'annotations', 'description', 'position') - def __init__(self, name): + def __init__(self, name, position=None): + #: Parameter name. self.name = name + + #: Parameter description or :const:`None`. + self.description = None + self.annotations = GtkDocAnnotations() + self.position = position + + def __repr__(self): + return '<GtkDocParameter %r %r>' % (self.name, self.annotations) + + def _validate_annotation(self, ann_name, options, required=False, + n_params=None, choices=None): + if required and len(options) == 0: + warn('%s annotation needs a value' % (ann_name, ), self.position) + return + + if n_params is not None: + if n_params == 0: + s = 'no value' + elif n_params == 1: + s = 'one value' + else: + s = '%d values' % (n_params, ) + if len(options) != n_params: + length = len(options) + warn('%s annotation needs %s, not %d' % (ann_name, s, length), + self.position) + return + + if choices is not None: + option = options[0] + if option not in choices: + warn('invalid %s annotation value: %r' % (ann_name, option, ), + self.position) + return + + def _validate_array(self, ann_name, options): + if len(options) == 0: + return + + for option, value in options.items(): + if option in [OPT_ARRAY_ZERO_TERMINATED, OPT_ARRAY_FIXED_SIZE]: + try: + int(value) + except (TypeError, ValueError): + if value is None: + warn('array option %s needs a value' % (option, ), + positions=self.position) + else: + warn('invalid array %s option value %r, ' + 'must be an integer' % (option, value, ), + positions=self.position) + elif option == OPT_ARRAY_LENGTH: + if value is None: + warn('array option length needs a value', + positions=self.position) + else: + warn('invalid array annotation value: %r' % (option, ), + self.position) + + def _validate_closure(self, ann_name, options): + if len(options) != 0 and len(options) > 1: + warn('closure takes at most 1 value, %d given' % (len(options), ), + self.position) + + def _validate_element_type(self, ann_name, options): + self._validate_annotation(ann_name, options, required=True) + if len(options) == 0: + warn('element-type takes at least one value, none given', + self.position) + return + if len(options) > 2: + warn('element-type takes at most 2 values, %d given' % (len(options), ), + self.position) + return + + def _validate_out(self, ann_name, options): + if len(options) == 0: + return + if len(options) > 1: + warn('out annotation takes at most 1 value, %d given' % (len(options), ), + self.position) + return + option = options[0] + if option not in [OPT_OUT_CALLEE_ALLOCATES, + OPT_OUT_CALLER_ALLOCATES]: + warn("out annotation value is invalid: %r" % (option, ), + self.position) + return + + def _get_gtk_doc_value(self): + def serialize_one(option, value, fmt, fmt2): + if value: + if type(value) != str: + if isinstance(value, list): + value = ' '.join(value) + else: + value = ' '.join((serialize_one(k, v, '%s=%s', '%s') + for k, v in value.items())) + return fmt % (option, value) + else: + return fmt2 % (option, ) + serialized = '' + annotations = [] + for ann_name, options in self.annotations.items(): + annotations.append(serialize_one(ann_name, options, '(%s %s)', '(%s)')) + if annotations: + serialized += ' '.join(annotations) + if self.description and annotations: + serialized += ': ' + return serialized + + def to_gtk_doc(self): + return '@%s: %s%s' % (self.name, self._get_gtk_doc_value(), self.description) + + def validate(self): + for ann_name, value in self.annotations.items(): + if ann_name == ANN_ALLOW_NONE: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name == ANN_ARRAY: + self._validate_array(ann_name, value) + elif ann_name == ANN_ATTRIBUTES: + # The 'attributes' annotation allows free form annotations. + pass + elif ann_name == ANN_CLOSURE: + self._validate_closure(ann_name, value) + elif ann_name == ANN_DESTROY: + self._validate_annotation(ann_name, value, n_params=1) + elif ann_name == ANN_ELEMENT_TYPE: + self._validate_element_type(ann_name, value) + elif ann_name == ANN_FOREIGN: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name == ANN_IN: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name in [ANN_INOUT, ANN_INOUT_ALT]: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name == ANN_OUT: + self._validate_out(ann_name, value) + elif ann_name == ANN_SCOPE: + self._validate_annotation( + ann_name, value, required=True, + n_params=1, + choices=[OPT_SCOPE_ASYNC, + OPT_SCOPE_CALL, + OPT_SCOPE_NOTIFIED]) + elif ann_name == ANN_SKIP: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name == ANN_TRANSFER: + self._validate_annotation( + ann_name, value, required=True, + n_params=1, + choices=[OPT_TRANSFER_FULL, + OPT_TRANSFER_CONTAINER, + OPT_TRANSFER_NONE, + OPT_TRANSFER_FLOATING]) + elif ann_name == ANN_TYPE: + self._validate_annotation(ann_name, value, required=True, + n_params=1) + elif ann_name == ANN_CONSTRUCTOR: + self._validate_annotation(ann_name, value, n_params=0) + elif ann_name == ANN_METHOD: + self._validate_annotation(ann_name, value, n_params=0) + else: + warn('unknown annotation: %s' % (ann_name, ), + self.position) + + +class GtkDocTag(object): + ''' + Represents a GTK-Doc tag part. + ''' + + __slots__ = ('name', 'annotations', 'value', 'description', 'position') + + def __init__(self, name, position=None): + #: Tag name. + self.name = name + + #: Tag value or :const:`None`. + self.value = None + + #: Tag description or :const:`None`. self.description = None - self.value = '' - self.position = None + + self.annotations = GtkDocAnnotations() + self.position = position def __repr__(self): return '<GtkDocTag %r %r>' % (self.name, self.annotations) @@ -610,10 +796,7 @@ class GtkDocTag(object): serialized += ': ' return serialized - def to_gtk_doc_param(self): - return '@%s: %s%s' % (self.name, self._get_gtk_doc_value(), self.description) - - def to_gtk_doc_tag(self): + def to_gtk_doc(self): return '%s: %s%s' % (self.name.capitalize(), self._get_gtk_doc_value(), self.description or '') @@ -729,7 +912,7 @@ class GtkDocCommentBlock(object): lines[0] += ' ' + annotations for param in self.params.values(): - lines.append(param.to_gtk_doc_param()) + lines.append(param.to_gtk_doc()) if self.description: lines.append('') for l in self.description.split('\n'): @@ -737,7 +920,7 @@ class GtkDocCommentBlock(object): if self.tags: lines.append('') for tag in self.tags.values(): - lines.append(tag.to_gtk_doc_tag()) + lines.append(tag.to_gtk_doc()) comment = '' comment += '/**\n' @@ -898,8 +1081,7 @@ class GtkDocCommentBlockParser(object): part_indent = None line_indent = None in_part = None - current_param = None - current_tag = None + current_part = None returns_seen = False for line_offset, line in comment_lines: @@ -1029,6 +1211,19 @@ class GtkDocCommentBlockParser(object): warn("encountered multiple 'Returns' parameters or tags for " "'%s'." % (comment_block.name, ), position) + + tag = GtkDocTag(TAG_RETURNS, position) + + if param_fields: + (a, d) = self._parse_fields(position, + column_offset + param_fields_start, + original_line, + param_fields) + tag.annotations = a + tag.description = d + comment_block.tags[TAG_RETURNS] = tag + current_part = tag + continue elif (param_name == 'Varargs' or (param_name.endswith('...') and param_name != '...')): # Deprecated @Varargs notation or named __VA_ARGS__ instead of @... @@ -1042,21 +1237,17 @@ class GtkDocCommentBlockParser(object): (param_name, comment_block.name, original_line, marker), position) - tag = GtkDocTag(param_name) - tag.position = position + parameter = GtkDocParameter(param_name, position) if param_fields: (a, d) = self._parse_fields(position, column_offset + param_fields_start, original_line, param_fields) - tag.annotations = a - tag.description = d + parameter.annotations = a + parameter.description = d - if param_name == TAG_RETURNS: - comment_block.tags[param_name] = tag - else: - comment_block.params[param_name] = tag - current_param = tag + comment_block.params[param_name] = parameter + current_part = parameter continue #################################################################### @@ -1180,8 +1371,7 @@ class GtkDocCommentBlockParser(object): "'%s'." % (comment_block.name, ), position) - tag = GtkDocTag(TAG_RETURNS) - tag.position = position + tag = GtkDocTag(TAG_RETURNS, position) if tag_fields: (a, d) = self._parse_fields(position, @@ -1192,7 +1382,7 @@ class GtkDocCommentBlockParser(object): tag.description = d comment_block.tags[TAG_RETURNS] = tag - current_tag = tag + current_part = tag continue else: if tag_name_lower in comment_block.tags.keys(): @@ -1200,8 +1390,7 @@ class GtkDocCommentBlockParser(object): (tag_name, comment_block.name, original_line, marker), position) - tag = GtkDocTag(tag_name_lower) - tag.position = position + tag = GtkDocTag(tag_name_lower, position) if tag_fields: (a, d) = self._parse_fields(position, @@ -1222,7 +1411,7 @@ class GtkDocCommentBlockParser(object): tag.description = result.group('description') comment_block.tags[tag_name_lower] = tag - current_tag = tag + current_part = tag continue #################################################################### @@ -1236,22 +1425,19 @@ class GtkDocCommentBlockParser(object): comment_block.description += '\n' + line continue elif in_part == PART_PARAMETERS: - if not current_param.description: + if not current_part.description: self._validate_multiline_annotation_continuation(line, original_line, column_offset, position) # Append to parameter description. - if current_param.description is None: - current_param.description = line - else: - current_param.description += ' ' + line.strip() + current_part.description += ' ' + line.strip() continue elif in_part == PART_TAGS: - if not current_tag.description: + if not current_part.description: self._validate_multiline_annotation_continuation(line, original_line, column_offset, position) # Append to tag description. - current_tag.description += ' ' + line.strip() + current_part.description += ' ' + line.strip() ######################################################################## # Finished parsing this comment block. @@ -1280,11 +1466,6 @@ class GtkDocCommentBlockParser(object): else: part.description = None - if part.value: - part.value = part.value.strip() - else: - part.value = '' - def _validate_multiline_annotation_continuation(self, line, original_line, column_offset, position): ''' |