4 # Copyright IBM, Corp. 2011
5 # Copyright (c) 2013-2015 Red Hat Inc.
8 # Anthony Liguori <aliguori@us.ibm.com>
9 # Markus Armbruster <armbru@redhat.com>
11 # This work is licensed under the terms of the GNU GPL, version 2.
12 # See the COPYING file in the top-level directory.
15 from ordereddict
import OrderedDict
23 'str': 'QTYPE_QSTRING',
25 'number': 'QTYPE_QFLOAT',
26 'bool': 'QTYPE_QBOOL',
28 'int16': 'QTYPE_QINT',
29 'int32': 'QTYPE_QINT',
30 'int64': 'QTYPE_QINT',
31 'uint8': 'QTYPE_QINT',
32 'uint16': 'QTYPE_QINT',
33 'uint32': 'QTYPE_QINT',
34 'uint64': 'QTYPE_QINT',
36 'any': None, # any qtype_code possible, actually
39 # Whitelist of commands allowed to return a non-dictionary
42 'human-monitor-command',
44 'query-migrate-cache-size',
51 'guest-fsfreeze-freeze',
52 'guest-fsfreeze-freeze-list',
53 'guest-fsfreeze-status',
54 'guest-fsfreeze-thaw',
58 'guest-sync-delimited',
68 # Parsing the schema into expressions
72 def error_path(parent
):
75 res
= ("In file included from %s:%d:\n" % (parent
['file'],
76 parent
['line'])) + res
77 parent
= parent
['parent']
81 class QAPISchemaError(Exception):
82 def __init__(self
, schema
, msg
):
83 Exception.__init
__(self
)
84 self
.fname
= schema
.fname
87 self
.line
= schema
.line
88 for ch
in schema
.src
[schema
.line_pos
:schema
.pos
]:
90 self
.col
= (self
.col
+ 7) % 8 + 1
93 self
.info
= schema
.incl_info
96 return error_path(self
.info
) + \
97 "%s:%d:%d: %s" % (self
.fname
, self
.line
, self
.col
, self
.msg
)
100 class QAPIExprError(Exception):
101 def __init__(self
, expr_info
, msg
):
102 Exception.__init
__(self
)
104 self
.info
= expr_info
108 return error_path(self
.info
['parent']) + \
109 "%s:%d: %s" % (self
.info
['file'], self
.info
['line'], self
.msg
)
112 class QAPISchemaParser(object):
114 def __init__(self
, fp
, previously_included
=[], incl_info
=None):
115 abs_fname
= os
.path
.abspath(fp
.name
)
118 previously_included
.append(abs_fname
)
119 self
.incl_info
= incl_info
121 if self
.src
== '' or self
.src
[-1] != '\n':
129 while self
.tok
is not None:
130 expr_info
= {'file': fname
, 'line': self
.line
,
131 'parent': self
.incl_info
}
132 expr
= self
.get_expr(False)
133 if isinstance(expr
, dict) and "include" in expr
:
135 raise QAPIExprError(expr_info
,
136 "Invalid 'include' directive")
137 include
= expr
["include"]
138 if not isinstance(include
, str):
139 raise QAPIExprError(expr_info
,
140 "Value of 'include' must be a string")
141 incl_abs_fname
= os
.path
.join(os
.path
.dirname(abs_fname
),
143 # catch inclusion cycle
146 if incl_abs_fname
== os
.path
.abspath(inf
['file']):
147 raise QAPIExprError(expr_info
, "Inclusion loop for %s"
150 # skip multiple include of the same file
151 if incl_abs_fname
in previously_included
:
154 fobj
= open(incl_abs_fname
, 'r')
156 raise QAPIExprError(expr_info
,
157 '%s: %s' % (e
.strerror
, include
))
158 exprs_include
= QAPISchemaParser(fobj
, previously_included
,
160 self
.exprs
.extend(exprs_include
.exprs
)
162 expr_elem
= {'expr': expr
,
164 self
.exprs
.append(expr_elem
)
168 self
.tok
= self
.src
[self
.cursor
]
169 self
.pos
= self
.cursor
174 self
.cursor
= self
.src
.find('\n', self
.cursor
)
175 elif self
.tok
in "{}:,[]":
177 elif self
.tok
== "'":
181 ch
= self
.src
[self
.cursor
]
184 raise QAPISchemaError(self
,
185 'Missing terminating "\'"')
199 for _
in range(0, 4):
200 ch
= self
.src
[self
.cursor
]
202 if ch
not in "0123456789abcdefABCDEF":
203 raise QAPISchemaError(self
,
204 '\\u escape needs 4 '
206 value
= (value
<< 4) + int(ch
, 16)
207 # If Python 2 and 3 didn't disagree so much on
208 # how to handle Unicode, then we could allow
209 # Unicode string defaults. But most of QAPI is
210 # ASCII-only, so we aren't losing much for now.
211 if not value
or value
> 0x7f:
212 raise QAPISchemaError(self
,
213 'For now, \\u escape '
214 'only supports non-zero '
215 'values up to \\u007f')
220 raise QAPISchemaError(self
,
221 "Unknown escape \\%s" % ch
)
230 elif self
.src
.startswith("true", self
.pos
):
234 elif self
.src
.startswith("false", self
.pos
):
238 elif self
.src
.startswith("null", self
.pos
):
242 elif self
.tok
== '\n':
243 if self
.cursor
== len(self
.src
):
247 self
.line_pos
= self
.cursor
248 elif not self
.tok
.isspace():
249 raise QAPISchemaError(self
, 'Stray "%s"' % self
.tok
)
251 def get_members(self
):
257 raise QAPISchemaError(self
, 'Expected string or "}"')
262 raise QAPISchemaError(self
, 'Expected ":"')
265 raise QAPISchemaError(self
, 'Duplicate key "%s"' % key
)
266 expr
[key
] = self
.get_expr(True)
271 raise QAPISchemaError(self
, 'Expected "," or "}"')
274 raise QAPISchemaError(self
, 'Expected string')
276 def get_values(self
):
281 if self
.tok
not in "{['tfn":
282 raise QAPISchemaError(self
, 'Expected "{", "[", "]", string, '
285 expr
.append(self
.get_expr(True))
290 raise QAPISchemaError(self
, 'Expected "," or "]"')
293 def get_expr(self
, nested
):
294 if self
.tok
!= '{' and not nested
:
295 raise QAPISchemaError(self
, 'Expected "{"')
298 expr
= self
.get_members()
299 elif self
.tok
== '[':
301 expr
= self
.get_values()
302 elif self
.tok
in "'tfn":
306 raise QAPISchemaError(self
, 'Expected "{", "[" or string')
310 # Semantic analysis of schema expressions
311 # TODO fold into QAPISchema
312 # TODO catching name collisions in generated code would be nice
316 def find_base_fields(base
):
317 base_struct_define
= find_struct(base
)
318 if not base_struct_define
:
320 return base_struct_define
['data']
323 # Return the qtype of an alternate branch, or None on error.
324 def find_alternate_member_qtype(qapi_type
):
325 if qapi_type
in builtin_types
:
326 return builtin_types
[qapi_type
]
327 elif find_struct(qapi_type
):
329 elif find_enum(qapi_type
):
330 return "QTYPE_QSTRING"
331 elif find_union(qapi_type
):
336 # Return the discriminator enum define if discriminator is specified as an
337 # enum type, otherwise return None.
338 def discriminator_find_enum_define(expr
):
339 base
= expr
.get('base')
340 discriminator
= expr
.get('discriminator')
342 if not (discriminator
and base
):
345 base_fields
= find_base_fields(base
)
349 discriminator_type
= base_fields
.get(discriminator
)
350 if not discriminator_type
:
353 return find_enum(discriminator_type
)
356 # FIXME should enforce "other than downstream extensions [...], all
357 # names should begin with a letter".
358 valid_name
= re
.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
361 def check_name(expr_info
, source
, name
, allow_optional
=False,
366 if not isinstance(name
, str):
367 raise QAPIExprError(expr_info
,
368 "%s requires a string name" % source
)
369 if name
.startswith('*'):
370 membername
= name
[1:]
371 if not allow_optional
:
372 raise QAPIExprError(expr_info
,
373 "%s does not allow optional name '%s'"
375 # Enum members can start with a digit, because the generated C
376 # code always prefixes it with the enum name
378 membername
= '_' + membername
379 # Reserve the entire 'q_' namespace for c_name()
380 if not valid_name
.match(membername
) or \
381 c_name(membername
, False).startswith('q_'):
382 raise QAPIExprError(expr_info
,
383 "%s uses invalid name '%s'" % (source
, name
))
386 def add_name(name
, info
, meta
, implicit
=False):
388 check_name(info
, "'%s'" % meta
, name
)
389 # FIXME should reject names that differ only in '_' vs. '.'
390 # vs. '-', because they're liable to clash in generated C.
391 if name
in all_names
:
392 raise QAPIExprError(info
,
393 "%s '%s' is already defined"
394 % (all_names
[name
], name
))
395 if not implicit
and (name
.endswith('Kind') or name
.endswith('List')):
396 raise QAPIExprError(info
,
397 "%s '%s' should not end in '%s'"
398 % (meta
, name
, name
[-4:]))
399 all_names
[name
] = meta
402 def add_struct(definition
, info
):
404 name
= definition
['struct']
405 add_name(name
, info
, 'struct')
406 struct_types
.append(definition
)
409 def find_struct(name
):
411 for struct
in struct_types
:
412 if struct
['struct'] == name
:
417 def add_union(definition
, info
):
419 name
= definition
['union']
420 add_name(name
, info
, 'union')
421 union_types
.append(definition
)
424 def find_union(name
):
426 for union
in union_types
:
427 if union
['union'] == name
:
432 def add_enum(name
, info
, enum_values
=None, implicit
=False):
434 add_name(name
, info
, 'enum', implicit
)
435 enum_types
.append({"enum_name": name
, "enum_values": enum_values
})
440 for enum
in enum_types
:
441 if enum
['enum_name'] == name
:
447 return find_enum(name
) is not None
450 def check_type(expr_info
, source
, value
, allow_array
=False,
451 allow_dict
=False, allow_optional
=False,
458 # Check if array type for value is okay
459 if isinstance(value
, list):
461 raise QAPIExprError(expr_info
,
462 "%s cannot be an array" % source
)
463 if len(value
) != 1 or not isinstance(value
[0], str):
464 raise QAPIExprError(expr_info
,
465 "%s: array type must contain single type name"
469 # Check if type name for value is okay
470 if isinstance(value
, str):
471 if value
not in all_names
:
472 raise QAPIExprError(expr_info
,
473 "%s uses unknown type '%s'"
475 if not all_names
[value
] in allow_metas
:
476 raise QAPIExprError(expr_info
,
477 "%s cannot use %s type '%s'"
478 % (source
, all_names
[value
], value
))
482 raise QAPIExprError(expr_info
,
483 "%s should be a type name" % source
)
485 if not isinstance(value
, OrderedDict
):
486 raise QAPIExprError(expr_info
,
487 "%s should be a dictionary or type name" % source
)
489 # value is a dictionary, check that each member is okay
490 for (key
, arg
) in value
.items():
491 check_name(expr_info
, "Member of %s" % source
, key
,
492 allow_optional
=allow_optional
)
493 if c_name(key
, False) == 'u' or c_name(key
, False).startswith('has_'):
494 raise QAPIExprError(expr_info
,
495 "Member of %s uses reserved name '%s'"
497 # Todo: allow dictionaries to represent default values of
498 # an optional argument.
499 check_type(expr_info
, "Member '%s' of %s" % (key
, source
), arg
,
501 allow_metas
=['built-in', 'union', 'alternate', 'struct',
505 def check_member_clash(expr_info
, base_name
, data
, source
=""):
506 base
= find_struct(base_name
)
508 base_members
= base
['data']
509 for key
in data
.keys():
510 if key
.startswith('*'):
512 if key
in base_members
or "*" + key
in base_members
:
513 raise QAPIExprError(expr_info
,
514 "Member name '%s'%s clashes with base '%s'"
515 % (key
, source
, base_name
))
517 check_member_clash(expr_info
, base
['base'], data
, source
)
520 def check_command(expr
, expr_info
):
521 name
= expr
['command']
523 check_type(expr_info
, "'data' for command '%s'" % name
,
524 expr
.get('data'), allow_dict
=True, allow_optional
=True,
525 allow_metas
=['struct'])
526 returns_meta
= ['union', 'struct']
527 if name
in returns_whitelist
:
528 returns_meta
+= ['built-in', 'alternate', 'enum']
529 check_type(expr_info
, "'returns' for command '%s'" % name
,
530 expr
.get('returns'), allow_array
=True,
531 allow_optional
=True, allow_metas
=returns_meta
)
534 def check_event(expr
, expr_info
):
538 if name
.upper() == 'MAX':
539 raise QAPIExprError(expr_info
, "Event name 'MAX' cannot be created")
541 check_type(expr_info
, "'data' for event '%s'" % name
,
542 expr
.get('data'), allow_dict
=True, allow_optional
=True,
543 allow_metas
=['struct'])
546 def check_union(expr
, expr_info
):
548 base
= expr
.get('base')
549 discriminator
= expr
.get('discriminator')
550 members
= expr
['data']
551 values
= {'MAX': '(automatic)', 'KIND': '(automatic)',
552 'TYPE': '(automatic)'}
554 # Two types of unions, determined by discriminator.
556 # With no discriminator it is a simple union.
557 if discriminator
is None:
559 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
561 raise QAPIExprError(expr_info
,
562 "Simple union '%s' must not have a base"
565 # Else, it's a flat union.
567 # The object must have a string member 'base'.
568 check_type(expr_info
, "'base' for union '%s'" % name
,
569 base
, allow_metas
=['struct'])
571 raise QAPIExprError(expr_info
,
572 "Flat union '%s' must have a base"
574 base_fields
= find_base_fields(base
)
577 # The value of member 'discriminator' must name a non-optional
578 # member of the base struct.
579 check_name(expr_info
, "Discriminator of flat union '%s'" % name
,
581 discriminator_type
= base_fields
.get(discriminator
)
582 if not discriminator_type
:
583 raise QAPIExprError(expr_info
,
584 "Discriminator '%s' is not a member of base "
586 % (discriminator
, base
))
587 enum_define
= find_enum(discriminator_type
)
588 allow_metas
= ['struct']
589 # Do not allow string discriminator
591 raise QAPIExprError(expr_info
,
592 "Discriminator '%s' must be of enumeration "
593 "type" % discriminator
)
596 for (key
, value
) in members
.items():
597 check_name(expr_info
, "Member of union '%s'" % name
, key
)
599 # Each value must name a known type; furthermore, in flat unions,
600 # branches must be a struct with no overlapping member names
601 check_type(expr_info
, "Member '%s' of union '%s'" % (key
, name
),
602 value
, allow_array
=not base
, allow_metas
=allow_metas
)
604 branch_struct
= find_struct(value
)
606 check_member_clash(expr_info
, base
, branch_struct
['data'],
607 " of branch '%s'" % key
)
609 # If the discriminator names an enum type, then all members
610 # of 'data' must also be members of the enum type, which in turn
611 # must not collide with the discriminator name.
613 if key
not in enum_define
['enum_values']:
614 raise QAPIExprError(expr_info
,
615 "Discriminator value '%s' is not found in "
617 (key
, enum_define
["enum_name"]))
618 if discriminator
in enum_define
['enum_values']:
619 raise QAPIExprError(expr_info
,
620 "Discriminator name '%s' collides with "
621 "enum value in '%s'" %
622 (discriminator
, enum_define
["enum_name"]))
624 # Otherwise, check for conflicts in the generated enum
626 c_key
= camel_to_upper(key
)
628 raise QAPIExprError(expr_info
,
629 "Union '%s' member '%s' clashes with '%s'"
630 % (name
, key
, values
[c_key
]))
634 def check_alternate(expr
, expr_info
):
635 name
= expr
['alternate']
636 members
= expr
['data']
637 values
= {'MAX': '(automatic)'}
641 for (key
, value
) in members
.items():
642 check_name(expr_info
, "Member of alternate '%s'" % name
, key
)
644 # Check for conflicts in the generated enum
645 c_key
= camel_to_upper(key
)
647 raise QAPIExprError(expr_info
,
648 "Alternate '%s' member '%s' clashes with '%s'"
649 % (name
, key
, values
[c_key
]))
652 # Ensure alternates have no type conflicts.
653 check_type(expr_info
, "Member '%s' of alternate '%s'" % (key
, name
),
655 allow_metas
=['built-in', 'union', 'struct', 'enum'])
656 qtype
= find_alternate_member_qtype(value
)
658 if qtype
in types_seen
:
659 raise QAPIExprError(expr_info
,
660 "Alternate '%s' member '%s' can't "
661 "be distinguished from member '%s'"
662 % (name
, key
, types_seen
[qtype
]))
663 types_seen
[qtype
] = key
666 def check_enum(expr
, expr_info
):
668 members
= expr
.get('data')
669 prefix
= expr
.get('prefix')
670 values
= {'MAX': '(automatic)'}
672 if not isinstance(members
, list):
673 raise QAPIExprError(expr_info
,
674 "Enum '%s' requires an array for 'data'" % name
)
675 if prefix
is not None and not isinstance(prefix
, str):
676 raise QAPIExprError(expr_info
,
677 "Enum '%s' requires a string for 'prefix'" % name
)
678 for member
in members
:
679 check_name(expr_info
, "Member of enum '%s'" % name
, member
,
681 key
= camel_to_upper(member
)
683 raise QAPIExprError(expr_info
,
684 "Enum '%s' member '%s' clashes with '%s'"
685 % (name
, member
, values
[key
]))
689 def check_struct(expr
, expr_info
):
690 name
= expr
['struct']
691 members
= expr
['data']
693 check_type(expr_info
, "'data' for struct '%s'" % name
, members
,
694 allow_dict
=True, allow_optional
=True)
695 check_type(expr_info
, "'base' for struct '%s'" % name
, expr
.get('base'),
696 allow_metas
=['struct'])
698 check_member_clash(expr_info
, expr
['base'], expr
['data'])
701 def check_keys(expr_elem
, meta
, required
, optional
=[]):
702 expr
= expr_elem
['expr']
703 info
= expr_elem
['info']
705 if not isinstance(name
, str):
706 raise QAPIExprError(info
,
707 "'%s' key must have a string value" % meta
)
708 required
= required
+ [meta
]
709 for (key
, value
) in expr
.items():
710 if key
not in required
and key
not in optional
:
711 raise QAPIExprError(info
,
712 "Unknown key '%s' in %s '%s'"
714 if (key
== 'gen' or key
== 'success-response') and value
is not False:
715 raise QAPIExprError(info
,
716 "'%s' of %s '%s' should only use false value"
720 raise QAPIExprError(info
,
721 "Key '%s' is missing from %s '%s'"
725 def check_exprs(exprs
):
728 # Learn the types and check for valid expression keys
729 for builtin
in builtin_types
.keys():
730 all_names
[builtin
] = 'built-in'
731 for expr_elem
in exprs
:
732 expr
= expr_elem
['expr']
733 info
= expr_elem
['info']
735 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
736 add_enum(expr
['enum'], info
, expr
['data'])
737 elif 'union' in expr
:
738 check_keys(expr_elem
, 'union', ['data'],
739 ['base', 'discriminator'])
740 add_union(expr
, info
)
741 elif 'alternate' in expr
:
742 check_keys(expr_elem
, 'alternate', ['data'])
743 add_name(expr
['alternate'], info
, 'alternate')
744 elif 'struct' in expr
:
745 check_keys(expr_elem
, 'struct', ['data'], ['base'])
746 add_struct(expr
, info
)
747 elif 'command' in expr
:
748 check_keys(expr_elem
, 'command', [],
749 ['data', 'returns', 'gen', 'success-response'])
750 add_name(expr
['command'], info
, 'command')
751 elif 'event' in expr
:
752 check_keys(expr_elem
, 'event', [], ['data'])
753 add_name(expr
['event'], info
, 'event')
755 raise QAPIExprError(expr_elem
['info'],
756 "Expression is missing metatype")
758 # Try again for hidden UnionKind enum
759 for expr_elem
in exprs
:
760 expr
= expr_elem
['expr']
762 if not discriminator_find_enum_define(expr
):
763 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
765 elif 'alternate' in expr
:
766 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
769 # Validate that exprs make sense
770 for expr_elem
in exprs
:
771 expr
= expr_elem
['expr']
772 info
= expr_elem
['info']
775 check_enum(expr
, info
)
776 elif 'union' in expr
:
777 check_union(expr
, info
)
778 elif 'alternate' in expr
:
779 check_alternate(expr
, info
)
780 elif 'struct' in expr
:
781 check_struct(expr
, info
)
782 elif 'command' in expr
:
783 check_command(expr
, info
)
784 elif 'event' in expr
:
785 check_event(expr
, info
)
787 assert False, 'unexpected meta type'
793 # Schema compiler frontend
796 class QAPISchemaEntity(object):
797 def __init__(self
, name
, info
):
798 assert isinstance(name
, str)
800 # For explicitly defined entities, info points to the (explicit)
801 # definition. For builtins (and their arrays), info is None.
802 # For implicitly defined entities, info points to a place that
803 # triggered the implicit definition (there may be more than one
808 return c_name(self
.name
)
810 def check(self
, schema
):
813 def is_implicit(self
):
816 def visit(self
, visitor
):
820 class QAPISchemaVisitor(object):
821 def visit_begin(self
, schema
):
827 def visit_needed(self
, entity
):
828 # Default to visiting everything
831 def visit_builtin_type(self
, name
, info
, json_type
):
834 def visit_enum_type(self
, name
, info
, values
, prefix
):
837 def visit_array_type(self
, name
, info
, element_type
):
840 def visit_object_type(self
, name
, info
, base
, members
, variants
):
843 def visit_object_type_flat(self
, name
, info
, members
, variants
):
846 def visit_alternate_type(self
, name
, info
, variants
):
849 def visit_command(self
, name
, info
, arg_type
, ret_type
,
850 gen
, success_response
):
853 def visit_event(self
, name
, info
, arg_type
):
857 class QAPISchemaType(QAPISchemaEntity
):
858 def c_type(self
, is_param
=False):
859 return c_name(self
.name
) + pointer_suffix
867 def alternate_qtype(self
):
869 'string': 'QTYPE_QSTRING',
870 'number': 'QTYPE_QFLOAT',
872 'boolean': 'QTYPE_QBOOL',
873 'object': 'QTYPE_QDICT'
875 return json2qtype
.get(self
.json_type())
878 class QAPISchemaBuiltinType(QAPISchemaType
):
879 def __init__(self
, name
, json_type
, c_type
, c_null
):
880 QAPISchemaType
.__init
__(self
, name
, None)
881 assert not c_type
or isinstance(c_type
, str)
882 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
884 self
._json
_type
_name
= json_type
885 self
._c
_type
_name
= c_type
886 self
._c
_null
_val
= c_null
891 def c_type(self
, is_param
=False):
892 if is_param
and self
.name
== 'str':
893 return 'const ' + self
._c
_type
_name
894 return self
._c
_type
_name
897 return self
._c
_null
_val
900 return self
._json
_type
_name
902 def visit(self
, visitor
):
903 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
906 class QAPISchemaEnumType(QAPISchemaType
):
907 def __init__(self
, name
, info
, values
, prefix
):
908 QAPISchemaType
.__init
__(self
, name
, info
)
910 assert isinstance(v
, str)
911 assert prefix
is None or isinstance(prefix
, str)
915 def check(self
, schema
):
916 assert len(set(self
.values
)) == len(self
.values
)
918 def is_implicit(self
):
919 # See QAPISchema._make_implicit_enum_type()
920 return self
.name
.endswith('Kind')
922 def c_type(self
, is_param
=False):
923 return c_name(self
.name
)
926 return c_enum_const(self
.name
, (self
.values
+ ['MAX'])[0],
932 def visit(self
, visitor
):
933 visitor
.visit_enum_type(self
.name
, self
.info
,
934 self
.values
, self
.prefix
)
937 class QAPISchemaArrayType(QAPISchemaType
):
938 def __init__(self
, name
, info
, element_type
):
939 QAPISchemaType
.__init
__(self
, name
, info
)
940 assert isinstance(element_type
, str)
941 self
._element
_type
_name
= element_type
942 self
.element_type
= None
944 def check(self
, schema
):
945 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
946 assert self
.element_type
948 def is_implicit(self
):
954 def visit(self
, visitor
):
955 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
958 class QAPISchemaObjectType(QAPISchemaType
):
959 def __init__(self
, name
, info
, base
, local_members
, variants
):
960 QAPISchemaType
.__init
__(self
, name
, info
)
961 assert base
is None or isinstance(base
, str)
962 for m
in local_members
:
963 assert isinstance(m
, QAPISchemaObjectTypeMember
)
964 assert (variants
is None or
965 isinstance(variants
, QAPISchemaObjectTypeVariants
))
966 self
._base
_name
= base
968 self
.local_members
= local_members
969 self
.variants
= variants
972 def check(self
, schema
):
973 assert self
.members
is not False # not running in cycles
976 self
.members
= False # mark as being checked
978 self
.base
= schema
.lookup_type(self
._base
_name
)
979 assert isinstance(self
.base
, QAPISchemaObjectType
)
980 assert not self
.base
.variants
# not implemented
981 self
.base
.check(schema
)
982 members
= list(self
.base
.members
)
987 assert c_name(m
.name
) not in seen
989 for m
in self
.local_members
:
990 m
.check(schema
, members
, seen
)
992 self
.variants
.check(schema
, members
, seen
)
993 self
.members
= members
995 def is_implicit(self
):
996 # See QAPISchema._make_implicit_object_type()
997 return self
.name
[0] == ':'
1000 assert not self
.is_implicit()
1001 return QAPISchemaType
.c_name(self
)
1003 def c_type(self
, is_param
=False):
1004 assert not self
.is_implicit()
1005 return QAPISchemaType
.c_type(self
)
1007 def json_type(self
):
1010 def visit(self
, visitor
):
1011 visitor
.visit_object_type(self
.name
, self
.info
,
1012 self
.base
, self
.local_members
, self
.variants
)
1013 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1014 self
.members
, self
.variants
)
1017 class QAPISchemaObjectTypeMember(object):
1018 def __init__(self
, name
, typ
, optional
):
1019 assert isinstance(name
, str)
1020 assert isinstance(typ
, str)
1021 assert isinstance(optional
, bool)
1023 self
._type
_name
= typ
1025 self
.optional
= optional
1027 def check(self
, schema
, all_members
, seen
):
1028 assert self
.name
not in seen
1029 self
.type = schema
.lookup_type(self
._type
_name
)
1031 all_members
.append(self
)
1032 seen
[self
.name
] = self
1035 class QAPISchemaObjectTypeVariants(object):
1036 def __init__(self
, tag_name
, tag_member
, variants
):
1037 # Flat unions pass tag_name but not tag_member.
1038 # Simple unions and alternates pass tag_member but not tag_name.
1039 # After check(), tag_member is always set, and tag_name remains
1040 # a reliable witness of being used by a flat union.
1041 assert bool(tag_member
) != bool(tag_name
)
1042 assert (isinstance(tag_name
, str) or
1043 isinstance(tag_member
, QAPISchemaObjectTypeMember
))
1045 assert isinstance(v
, QAPISchemaObjectTypeVariant
)
1046 self
.tag_name
= tag_name
1047 self
.tag_member
= tag_member
1048 self
.variants
= variants
1050 def check(self
, schema
, members
, seen
):
1052 self
.tag_member
= seen
[self
.tag_name
]
1054 self
.tag_member
.check(schema
, members
, seen
)
1055 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1056 for v
in self
.variants
:
1058 v
.check(schema
, self
.tag_member
.type, vseen
)
1061 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1062 def __init__(self
, name
, typ
):
1063 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1065 def check(self
, schema
, tag_type
, seen
):
1066 QAPISchemaObjectTypeMember
.check(self
, schema
, [], seen
)
1067 assert self
.name
in tag_type
.values
1069 # This function exists to support ugly simple union special cases
1070 # TODO get rid of them, and drop the function
1071 def simple_union_type(self
):
1072 if (self
.type.is_implicit() and
1073 isinstance(self
.type, QAPISchemaObjectType
)):
1074 assert len(self
.type.members
) == 1
1075 assert not self
.type.variants
1076 return self
.type.members
[0].type
1080 class QAPISchemaAlternateType(QAPISchemaType
):
1081 def __init__(self
, name
, info
, variants
):
1082 QAPISchemaType
.__init
__(self
, name
, info
)
1083 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1084 assert not variants
.tag_name
1085 self
.variants
= variants
1087 def check(self
, schema
):
1088 self
.variants
.check(schema
, [], {})
1090 def json_type(self
):
1093 def visit(self
, visitor
):
1094 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1097 class QAPISchemaCommand(QAPISchemaEntity
):
1098 def __init__(self
, name
, info
, arg_type
, ret_type
, gen
, success_response
):
1099 QAPISchemaEntity
.__init
__(self
, name
, info
)
1100 assert not arg_type
or isinstance(arg_type
, str)
1101 assert not ret_type
or isinstance(ret_type
, str)
1102 self
._arg
_type
_name
= arg_type
1103 self
.arg_type
= None
1104 self
._ret
_type
_name
= ret_type
1105 self
.ret_type
= None
1107 self
.success_response
= success_response
1109 def check(self
, schema
):
1110 if self
._arg
_type
_name
:
1111 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1112 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1113 assert not self
.arg_type
.variants
# not implemented
1114 if self
._ret
_type
_name
:
1115 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1116 assert isinstance(self
.ret_type
, QAPISchemaType
)
1118 def visit(self
, visitor
):
1119 visitor
.visit_command(self
.name
, self
.info
,
1120 self
.arg_type
, self
.ret_type
,
1121 self
.gen
, self
.success_response
)
1124 class QAPISchemaEvent(QAPISchemaEntity
):
1125 def __init__(self
, name
, info
, arg_type
):
1126 QAPISchemaEntity
.__init
__(self
, name
, info
)
1127 assert not arg_type
or isinstance(arg_type
, str)
1128 self
._arg
_type
_name
= arg_type
1129 self
.arg_type
= None
1131 def check(self
, schema
):
1132 if self
._arg
_type
_name
:
1133 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1134 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1135 assert not self
.arg_type
.variants
# not implemented
1137 def visit(self
, visitor
):
1138 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
)
1141 class QAPISchema(object):
1142 def __init__(self
, fname
):
1144 self
.exprs
= check_exprs(QAPISchemaParser(open(fname
, "r")).exprs
)
1145 self
._entity
_dict
= {}
1146 self
._predefining
= True
1147 self
._def
_predefineds
()
1148 self
._predefining
= False
1151 except (QAPISchemaError
, QAPIExprError
), err
:
1152 print >>sys
.stderr
, err
1155 def _def_entity(self
, ent
):
1156 # Only the predefined types are allowed to not have info
1157 assert ent
.info
or self
._predefining
1158 assert ent
.name
not in self
._entity
_dict
1159 self
._entity
_dict
[ent
.name
] = ent
1161 def lookup_entity(self
, name
, typ
=None):
1162 ent
= self
._entity
_dict
.get(name
)
1163 if typ
and not isinstance(ent
, typ
):
1167 def lookup_type(self
, name
):
1168 return self
.lookup_entity(name
, QAPISchemaType
)
1170 def _def_builtin_type(self
, name
, json_type
, c_type
, c_null
):
1171 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
,
1173 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1174 # qapi-types.h from a single .c, all arrays of builtins must be
1175 # declared in the first file whether or not they are used. Nicer
1176 # would be to use lazy instantiation, while figuring out how to
1177 # avoid compilation issues with multiple qapi-types.h.
1178 self
._make
_array
_type
(name
, None)
1180 def _def_predefineds(self
):
1181 for t
in [('str', 'string', 'char' + pointer_suffix
, 'NULL'),
1182 ('number', 'number', 'double', '0'),
1183 ('int', 'int', 'int64_t', '0'),
1184 ('int8', 'int', 'int8_t', '0'),
1185 ('int16', 'int', 'int16_t', '0'),
1186 ('int32', 'int', 'int32_t', '0'),
1187 ('int64', 'int', 'int64_t', '0'),
1188 ('uint8', 'int', 'uint8_t', '0'),
1189 ('uint16', 'int', 'uint16_t', '0'),
1190 ('uint32', 'int', 'uint32_t', '0'),
1191 ('uint64', 'int', 'uint64_t', '0'),
1192 ('size', 'int', 'uint64_t', '0'),
1193 ('bool', 'boolean', 'bool', 'false'),
1194 ('any', 'value', 'QObject' + pointer_suffix
, 'NULL')]:
1195 self
._def
_builtin
_type
(*t
)
1196 self
.the_empty_object_type
= QAPISchemaObjectType(':empty', None, None,
1198 self
._def
_entity
(self
.the_empty_object_type
)
1200 def _make_implicit_enum_type(self
, name
, info
, values
):
1201 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1202 self
._def
_entity
(QAPISchemaEnumType(name
, info
, values
, None))
1205 def _make_array_type(self
, element_type
, info
):
1206 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1207 if not self
.lookup_type(name
):
1208 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1211 def _make_implicit_object_type(self
, name
, info
, role
, members
):
1214 name
= ':obj-%s-%s' % (name
, role
)
1215 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1216 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None,
1220 def _def_enum_type(self
, expr
, info
):
1223 prefix
= expr
.get('prefix')
1224 self
._def
_entity
(QAPISchemaEnumType(name
, info
, data
, prefix
))
1226 def _make_member(self
, name
, typ
, info
):
1228 if name
.startswith('*'):
1231 if isinstance(typ
, list):
1232 assert len(typ
) == 1
1233 typ
= self
._make
_array
_type
(typ
[0], info
)
1234 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1236 def _make_members(self
, data
, info
):
1237 return [self
._make
_member
(key
, value
, info
)
1238 for (key
, value
) in data
.iteritems()]
1240 def _def_struct_type(self
, expr
, info
):
1241 name
= expr
['struct']
1242 base
= expr
.get('base')
1244 self
._def
_entity
(QAPISchemaObjectType(name
, info
, base
,
1245 self
._make
_members
(data
, info
),
1248 def _make_variant(self
, case
, typ
):
1249 return QAPISchemaObjectTypeVariant(case
, typ
)
1251 def _make_simple_variant(self
, case
, typ
, info
):
1252 if isinstance(typ
, list):
1253 assert len(typ
) == 1
1254 typ
= self
._make
_array
_type
(typ
[0], info
)
1255 typ
= self
._make
_implicit
_object
_type
(
1256 typ
, info
, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1257 return QAPISchemaObjectTypeVariant(case
, typ
)
1259 def _make_implicit_tag(self
, type_name
, info
, variants
):
1260 typ
= self
._make
_implicit
_enum
_type
(type_name
, info
,
1261 [v
.name
for v
in variants
])
1262 return QAPISchemaObjectTypeMember('type', typ
, False)
1264 def _def_union_type(self
, expr
, info
):
1265 name
= expr
['union']
1267 base
= expr
.get('base')
1268 tag_name
= expr
.get('discriminator')
1271 variants
= [self
._make
_variant
(key
, value
)
1272 for (key
, value
) in data
.iteritems()]
1274 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1275 for (key
, value
) in data
.iteritems()]
1276 tag_member
= self
._make
_implicit
_tag
(name
, info
, variants
)
1278 QAPISchemaObjectType(name
, info
, base
,
1279 self
._make
_members
(OrderedDict(), info
),
1280 QAPISchemaObjectTypeVariants(tag_name
,
1284 def _def_alternate_type(self
, expr
, info
):
1285 name
= expr
['alternate']
1287 variants
= [self
._make
_variant
(key
, value
)
1288 for (key
, value
) in data
.iteritems()]
1289 tag_member
= self
._make
_implicit
_tag
(name
, info
, variants
)
1291 QAPISchemaAlternateType(name
, info
,
1292 QAPISchemaObjectTypeVariants(None,
1296 def _def_command(self
, expr
, info
):
1297 name
= expr
['command']
1298 data
= expr
.get('data')
1299 rets
= expr
.get('returns')
1300 gen
= expr
.get('gen', True)
1301 success_response
= expr
.get('success-response', True)
1302 if isinstance(data
, OrderedDict
):
1303 data
= self
._make
_implicit
_object
_type
(
1304 name
, info
, 'arg', self
._make
_members
(data
, info
))
1305 if isinstance(rets
, list):
1306 assert len(rets
) == 1
1307 rets
= self
._make
_array
_type
(rets
[0], info
)
1308 self
._def
_entity
(QAPISchemaCommand(name
, info
, data
, rets
, gen
,
1311 def _def_event(self
, expr
, info
):
1312 name
= expr
['event']
1313 data
= expr
.get('data')
1314 if isinstance(data
, OrderedDict
):
1315 data
= self
._make
_implicit
_object
_type
(
1316 name
, info
, 'arg', self
._make
_members
(data
, info
))
1317 self
._def
_entity
(QAPISchemaEvent(name
, info
, data
))
1319 def _def_exprs(self
):
1320 for expr_elem
in self
.exprs
:
1321 expr
= expr_elem
['expr']
1322 info
= expr_elem
['info']
1324 self
._def
_enum
_type
(expr
, info
)
1325 elif 'struct' in expr
:
1326 self
._def
_struct
_type
(expr
, info
)
1327 elif 'union' in expr
:
1328 self
._def
_union
_type
(expr
, info
)
1329 elif 'alternate' in expr
:
1330 self
._def
_alternate
_type
(expr
, info
)
1331 elif 'command' in expr
:
1332 self
._def
_command
(expr
, info
)
1333 elif 'event' in expr
:
1334 self
._def
_event
(expr
, info
)
1339 for ent
in self
._entity
_dict
.values():
1342 def visit(self
, visitor
):
1343 visitor
.visit_begin(self
)
1344 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1345 if visitor
.visit_needed(entity
):
1346 entity
.visit(visitor
)
1351 # Code generation helpers
1354 def camel_case(name
):
1358 if ch
in ['_', '-']:
1361 new_name
+= ch
.upper()
1364 new_name
+= ch
.lower()
1368 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1369 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1370 # ENUM24_Name -> ENUM24_NAME
1371 def camel_to_upper(value
):
1372 c_fun_str
= c_name(value
, False)
1380 # When c is upper and no "_" appears before, do more checks
1381 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != "_":
1382 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1384 elif c_fun_str
[i
- 1].isdigit():
1387 return new_name
.lstrip('_').upper()
1390 def c_enum_const(type_name
, const_name
, prefix
=None):
1391 if prefix
is not None:
1393 return camel_to_upper(type_name
+ '_' + const_name
)
1395 c_name_trans
= string
.maketrans('.-', '__')
1398 # Map @name to a valid C identifier.
1399 # If @protect, avoid returning certain ticklish identifiers (like
1400 # C keywords) by prepending "q_".
1402 # Used for converting 'name' from a 'name':'type' qapi definition
1403 # into a generated struct member, as well as converting type names
1404 # into substrings of a generated C function name.
1405 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1406 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1407 def c_name(name
, protect
=True):
1408 # ANSI X3J11/88-090, 3.1.1
1409 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1410 'default', 'do', 'double', 'else', 'enum', 'extern',
1411 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1412 'return', 'short', 'signed', 'sizeof', 'static',
1413 'struct', 'switch', 'typedef', 'union', 'unsigned',
1414 'void', 'volatile', 'while'])
1415 # ISO/IEC 9899:1999, 6.4.1
1416 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1417 # ISO/IEC 9899:2011, 6.4.1
1418 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1419 '_Noreturn', '_Static_assert', '_Thread_local'])
1420 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1422 gcc_words
= set(['asm', 'typeof'])
1423 # C++ ISO/IEC 14882:2003 2.11
1424 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1425 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1426 'namespace', 'new', 'operator', 'private', 'protected',
1427 'public', 'reinterpret_cast', 'static_cast', 'template',
1428 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1429 'using', 'virtual', 'wchar_t',
1430 # alternative representations
1431 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1432 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1433 # namespace pollution:
1434 polluted_words
= set(['unix', 'errno'])
1435 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1436 | cpp_words | polluted_words
):
1438 return name
.translate(c_name_trans
)
1440 eatspace
= '\033EATSPACE.'
1441 pointer_suffix
= ' *' + eatspace
1444 def genindent(count
):
1446 for _
in range(count
):
1453 def push_indent(indent_amount
=4):
1455 indent_level
+= indent_amount
1458 def pop_indent(indent_amount
=4):
1460 indent_level
-= indent_amount
1463 # Generate @code with @kwds interpolated.
1464 # Obey indent_level, and strip eatspace.
1465 def cgen(code
, **kwds
):
1468 indent
= genindent(indent_level
)
1469 # re.subn() lacks flags support before Python 2.7, use re.compile()
1470 raw
= re
.subn(re
.compile("^.", re
.MULTILINE
),
1471 indent
+ r
'\g<0>', raw
)
1473 return re
.sub(re
.escape(eatspace
) + ' *', '', raw
)
1476 def mcgen(code
, **kwds
):
1479 return cgen(code
, **kwds
)
1482 def guardname(filename
):
1483 return c_name(filename
, protect
=False).upper()
1486 def guardstart(name
):
1493 name
=guardname(name
))
1499 #endif /* %(name)s */
1502 name
=guardname(name
))
1505 def gen_enum_lookup(name
, values
, prefix
=None):
1508 const char *const %(c_name)s_lookup[] = {
1510 c_name
=c_name(name
))
1511 for value
in values
:
1512 index
= c_enum_const(name
, value
, prefix
)
1514 [%(index)s] = "%(value)s",
1516 index
=index
, value
=value
)
1518 max_index
= c_enum_const(name
, 'MAX', prefix
)
1520 [%(max_index)s] = NULL,
1523 max_index
=max_index
)
1527 def gen_enum(name
, values
, prefix
=None):
1528 # append automatically generated _MAX value
1529 enum_values
= values
+ ['MAX']
1533 typedef enum %(c_name)s {
1535 c_name
=c_name(name
))
1538 for value
in enum_values
:
1542 c_enum
=c_enum_const(name
, value
, prefix
),
1549 c_name
=c_name(name
))
1553 extern const char *const %(c_name)s_lookup[];
1555 c_name
=c_name(name
))
1559 def gen_params(arg_type
, extra
):
1562 assert not arg_type
.variants
1565 for memb
in arg_type
.members
:
1569 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1570 ret
+= '%s %s' % (memb
.type.c_type(is_param
=True), c_name(memb
.name
))
1576 def gen_err_check(label
='out', skiperr
=False):
1587 def gen_visit_fields(members
, prefix
='', need_cast
=False, skiperr
=False):
1594 for memb
in members
:
1597 visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s", %(errp)s);
1599 prefix
=prefix
, c_name
=c_name(memb
.name
),
1600 name
=memb
.name
, errp
=errparg
)
1601 ret
+= gen_err_check(skiperr
=skiperr
)
1603 if (%(prefix)shas_%(c_name)s) {
1605 prefix
=prefix
, c_name
=c_name(memb
.name
))
1608 # Ugly: sometimes we need to cast away const
1609 if need_cast
and memb
.type.name
== 'str':
1615 visit_type_%(c_type)s(v, %(cast)s&%(prefix)s%(c_name)s, "%(name)s", %(errp)s);
1617 c_type
=memb
.type.c_name(), prefix
=prefix
, cast
=cast
,
1618 c_name
=c_name(memb
.name
), name
=memb
.name
,
1620 ret
+= gen_err_check(skiperr
=skiperr
)
1631 # Common command line parsing
1635 def parse_command_line(extra_options
="", extra_long_options
=[]):
1638 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1639 "chp:o:" + extra_options
,
1640 ["source", "header", "prefix=",
1641 "output-dir="] + extra_long_options
)
1642 except getopt
.GetoptError
, err
:
1643 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1654 if o
in ("-p", "--prefix"):
1655 match
= re
.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1656 if match
.end() != len(a
):
1657 print >>sys
.stderr
, \
1658 "%s: 'funny character '%s' in argument of --prefix" \
1659 % (sys
.argv
[0], a
[match
.end()])
1662 elif o
in ("-o", "--output-dir"):
1663 output_dir
= a
+ "/"
1664 elif o
in ("-c", "--source"):
1666 elif o
in ("-h", "--header"):
1669 extra_opts
.append(oa
)
1671 if not do_c
and not do_h
:
1676 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1680 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1683 # Generate output files with boilerplate
1687 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
1688 c_comment
, h_comment
):
1689 guard
= guardname(prefix
+ h_file
)
1690 c_file
= output_dir
+ prefix
+ c_file
1691 h_file
= output_dir
+ prefix
+ h_file
1695 os
.makedirs(output_dir
)
1697 if e
.errno
!= errno
.EEXIST
:
1700 def maybe_open(really
, name
, opt
):
1702 return open(name
, opt
)
1705 return StringIO
.StringIO()
1707 fdef
= maybe_open(do_c
, c_file
, 'w')
1708 fdecl
= maybe_open(do_h
, h_file
, 'w')
1710 fdef
.write(mcgen('''
1711 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1716 fdecl
.write(mcgen('''
1717 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1723 comment
=h_comment
, guard
=guard
))
1725 return (fdef
, fdecl
)
1728 def close_output(fdef
, fdecl
):