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)'}
553 # Two types of unions, determined by discriminator.
555 # With no discriminator it is a simple union.
556 if discriminator
is None:
558 allow_metas
= ['built-in', 'union', 'alternate', 'struct', 'enum']
560 raise QAPIExprError(expr_info
,
561 "Simple union '%s' must not have a base"
564 # Else, it's a flat union.
566 # The object must have a string member 'base'.
567 check_type(expr_info
, "'base' for union '%s'" % name
,
568 base
, allow_metas
=['struct'])
570 raise QAPIExprError(expr_info
,
571 "Flat union '%s' must have a base"
573 base_fields
= find_base_fields(base
)
576 # The value of member 'discriminator' must name a non-optional
577 # member of the base struct.
578 check_name(expr_info
, "Discriminator of flat union '%s'" % name
,
580 discriminator_type
= base_fields
.get(discriminator
)
581 if not discriminator_type
:
582 raise QAPIExprError(expr_info
,
583 "Discriminator '%s' is not a member of base "
585 % (discriminator
, base
))
586 enum_define
= find_enum(discriminator_type
)
587 allow_metas
= ['struct']
588 # Do not allow string discriminator
590 raise QAPIExprError(expr_info
,
591 "Discriminator '%s' must be of enumeration "
592 "type" % discriminator
)
595 for (key
, value
) in members
.items():
596 check_name(expr_info
, "Member of union '%s'" % name
, key
)
598 # Each value must name a known type; furthermore, in flat unions,
599 # branches must be a struct with no overlapping member names
600 check_type(expr_info
, "Member '%s' of union '%s'" % (key
, name
),
601 value
, allow_array
=not base
, allow_metas
=allow_metas
)
603 branch_struct
= find_struct(value
)
605 check_member_clash(expr_info
, base
, branch_struct
['data'],
606 " of branch '%s'" % key
)
608 # If the discriminator names an enum type, then all members
609 # of 'data' must also be members of the enum type.
611 if key
not in enum_define
['enum_values']:
612 raise QAPIExprError(expr_info
,
613 "Discriminator value '%s' is not found in "
615 (key
, enum_define
["enum_name"]))
617 # Otherwise, check for conflicts in the generated enum
619 c_key
= camel_to_upper(key
)
621 raise QAPIExprError(expr_info
,
622 "Union '%s' member '%s' clashes with '%s'"
623 % (name
, key
, values
[c_key
]))
627 def check_alternate(expr
, expr_info
):
628 name
= expr
['alternate']
629 members
= expr
['data']
630 values
= {'MAX': '(automatic)'}
634 for (key
, value
) in members
.items():
635 check_name(expr_info
, "Member of alternate '%s'" % name
, key
)
637 # Check for conflicts in the generated enum
638 c_key
= camel_to_upper(key
)
640 raise QAPIExprError(expr_info
,
641 "Alternate '%s' member '%s' clashes with '%s'"
642 % (name
, key
, values
[c_key
]))
645 # Ensure alternates have no type conflicts.
646 check_type(expr_info
, "Member '%s' of alternate '%s'" % (key
, name
),
648 allow_metas
=['built-in', 'union', 'struct', 'enum'])
649 qtype
= find_alternate_member_qtype(value
)
651 if qtype
in types_seen
:
652 raise QAPIExprError(expr_info
,
653 "Alternate '%s' member '%s' can't "
654 "be distinguished from member '%s'"
655 % (name
, key
, types_seen
[qtype
]))
656 types_seen
[qtype
] = key
659 def check_enum(expr
, expr_info
):
661 members
= expr
.get('data')
662 prefix
= expr
.get('prefix')
663 values
= {'MAX': '(automatic)'}
665 if not isinstance(members
, list):
666 raise QAPIExprError(expr_info
,
667 "Enum '%s' requires an array for 'data'" % name
)
668 if prefix
is not None and not isinstance(prefix
, str):
669 raise QAPIExprError(expr_info
,
670 "Enum '%s' requires a string for 'prefix'" % name
)
671 for member
in members
:
672 check_name(expr_info
, "Member of enum '%s'" % name
, member
,
674 key
= camel_to_upper(member
)
676 raise QAPIExprError(expr_info
,
677 "Enum '%s' member '%s' clashes with '%s'"
678 % (name
, member
, values
[key
]))
682 def check_struct(expr
, expr_info
):
683 name
= expr
['struct']
684 members
= expr
['data']
686 check_type(expr_info
, "'data' for struct '%s'" % name
, members
,
687 allow_dict
=True, allow_optional
=True)
688 check_type(expr_info
, "'base' for struct '%s'" % name
, expr
.get('base'),
689 allow_metas
=['struct'])
691 check_member_clash(expr_info
, expr
['base'], expr
['data'])
694 def check_keys(expr_elem
, meta
, required
, optional
=[]):
695 expr
= expr_elem
['expr']
696 info
= expr_elem
['info']
698 if not isinstance(name
, str):
699 raise QAPIExprError(info
,
700 "'%s' key must have a string value" % meta
)
701 required
= required
+ [meta
]
702 for (key
, value
) in expr
.items():
703 if key
not in required
and key
not in optional
:
704 raise QAPIExprError(info
,
705 "Unknown key '%s' in %s '%s'"
707 if (key
== 'gen' or key
== 'success-response') and value
is not False:
708 raise QAPIExprError(info
,
709 "'%s' of %s '%s' should only use false value"
713 raise QAPIExprError(info
,
714 "Key '%s' is missing from %s '%s'"
718 def check_exprs(exprs
):
721 # Learn the types and check for valid expression keys
722 for builtin
in builtin_types
.keys():
723 all_names
[builtin
] = 'built-in'
724 for expr_elem
in exprs
:
725 expr
= expr_elem
['expr']
726 info
= expr_elem
['info']
728 check_keys(expr_elem
, 'enum', ['data'], ['prefix'])
729 add_enum(expr
['enum'], info
, expr
['data'])
730 elif 'union' in expr
:
731 check_keys(expr_elem
, 'union', ['data'],
732 ['base', 'discriminator'])
733 add_union(expr
, info
)
734 elif 'alternate' in expr
:
735 check_keys(expr_elem
, 'alternate', ['data'])
736 add_name(expr
['alternate'], info
, 'alternate')
737 elif 'struct' in expr
:
738 check_keys(expr_elem
, 'struct', ['data'], ['base'])
739 add_struct(expr
, info
)
740 elif 'command' in expr
:
741 check_keys(expr_elem
, 'command', [],
742 ['data', 'returns', 'gen', 'success-response'])
743 add_name(expr
['command'], info
, 'command')
744 elif 'event' in expr
:
745 check_keys(expr_elem
, 'event', [], ['data'])
746 add_name(expr
['event'], info
, 'event')
748 raise QAPIExprError(expr_elem
['info'],
749 "Expression is missing metatype")
751 # Try again for hidden UnionKind enum
752 for expr_elem
in exprs
:
753 expr
= expr_elem
['expr']
755 if not discriminator_find_enum_define(expr
):
756 add_enum('%sKind' % expr
['union'], expr_elem
['info'],
758 elif 'alternate' in expr
:
759 add_enum('%sKind' % expr
['alternate'], expr_elem
['info'],
762 # Validate that exprs make sense
763 for expr_elem
in exprs
:
764 expr
= expr_elem
['expr']
765 info
= expr_elem
['info']
768 check_enum(expr
, info
)
769 elif 'union' in expr
:
770 check_union(expr
, info
)
771 elif 'alternate' in expr
:
772 check_alternate(expr
, info
)
773 elif 'struct' in expr
:
774 check_struct(expr
, info
)
775 elif 'command' in expr
:
776 check_command(expr
, info
)
777 elif 'event' in expr
:
778 check_event(expr
, info
)
780 assert False, 'unexpected meta type'
786 # Schema compiler frontend
789 class QAPISchemaEntity(object):
790 def __init__(self
, name
, info
):
791 assert isinstance(name
, str)
793 # For explicitly defined entities, info points to the (explicit)
794 # definition. For builtins (and their arrays), info is None.
795 # For implicitly defined entities, info points to a place that
796 # triggered the implicit definition (there may be more than one
801 return c_name(self
.name
)
803 def check(self
, schema
):
806 def is_implicit(self
):
809 def visit(self
, visitor
):
813 class QAPISchemaVisitor(object):
814 def visit_begin(self
, schema
):
820 def visit_needed(self
, entity
):
821 # Default to visiting everything
824 def visit_builtin_type(self
, name
, info
, json_type
):
827 def visit_enum_type(self
, name
, info
, values
, prefix
):
830 def visit_array_type(self
, name
, info
, element_type
):
833 def visit_object_type(self
, name
, info
, base
, members
, variants
):
836 def visit_object_type_flat(self
, name
, info
, members
, variants
):
839 def visit_alternate_type(self
, name
, info
, variants
):
842 def visit_command(self
, name
, info
, arg_type
, ret_type
,
843 gen
, success_response
):
846 def visit_event(self
, name
, info
, arg_type
):
850 class QAPISchemaType(QAPISchemaEntity
):
851 def c_type(self
, is_param
=False):
852 return c_name(self
.name
) + pointer_suffix
860 def alternate_qtype(self
):
862 'string': 'QTYPE_QSTRING',
863 'number': 'QTYPE_QFLOAT',
865 'boolean': 'QTYPE_QBOOL',
866 'object': 'QTYPE_QDICT'
868 return json2qtype
.get(self
.json_type())
871 class QAPISchemaBuiltinType(QAPISchemaType
):
872 def __init__(self
, name
, json_type
, c_type
, c_null
):
873 QAPISchemaType
.__init
__(self
, name
, None)
874 assert not c_type
or isinstance(c_type
, str)
875 assert json_type
in ('string', 'number', 'int', 'boolean', 'null',
877 self
._json
_type
_name
= json_type
878 self
._c
_type
_name
= c_type
879 self
._c
_null
_val
= c_null
884 def c_type(self
, is_param
=False):
885 if is_param
and self
.name
== 'str':
886 return 'const ' + self
._c
_type
_name
887 return self
._c
_type
_name
890 return self
._c
_null
_val
893 return self
._json
_type
_name
895 def visit(self
, visitor
):
896 visitor
.visit_builtin_type(self
.name
, self
.info
, self
.json_type())
899 class QAPISchemaEnumType(QAPISchemaType
):
900 def __init__(self
, name
, info
, values
, prefix
):
901 QAPISchemaType
.__init
__(self
, name
, info
)
903 assert isinstance(v
, str)
904 assert prefix
is None or isinstance(prefix
, str)
908 def check(self
, schema
):
909 assert len(set(self
.values
)) == len(self
.values
)
911 def is_implicit(self
):
912 # See QAPISchema._make_implicit_enum_type()
913 return self
.name
.endswith('Kind')
915 def c_type(self
, is_param
=False):
916 return c_name(self
.name
)
919 return c_enum_const(self
.name
, (self
.values
+ ['MAX'])[0],
925 def visit(self
, visitor
):
926 visitor
.visit_enum_type(self
.name
, self
.info
,
927 self
.values
, self
.prefix
)
930 class QAPISchemaArrayType(QAPISchemaType
):
931 def __init__(self
, name
, info
, element_type
):
932 QAPISchemaType
.__init
__(self
, name
, info
)
933 assert isinstance(element_type
, str)
934 self
._element
_type
_name
= element_type
935 self
.element_type
= None
937 def check(self
, schema
):
938 self
.element_type
= schema
.lookup_type(self
._element
_type
_name
)
939 assert self
.element_type
941 def is_implicit(self
):
947 def visit(self
, visitor
):
948 visitor
.visit_array_type(self
.name
, self
.info
, self
.element_type
)
951 class QAPISchemaObjectType(QAPISchemaType
):
952 def __init__(self
, name
, info
, base
, local_members
, variants
):
953 # struct has local_members, optional base, and no variants
954 # flat union has base, variants, and no local_members
955 # simple union has local_members, variants, and no base
956 QAPISchemaType
.__init
__(self
, name
, info
)
957 assert base
is None or isinstance(base
, str)
958 for m
in local_members
:
959 assert isinstance(m
, QAPISchemaObjectTypeMember
)
960 assert (variants
is None or
961 isinstance(variants
, QAPISchemaObjectTypeVariants
))
962 self
._base
_name
= base
964 self
.local_members
= local_members
965 self
.variants
= variants
968 def check(self
, schema
):
969 assert self
.members
is not False # not running in cycles
972 self
.members
= False # mark as being checked
975 self
.base
= schema
.lookup_type(self
._base
_name
)
976 assert isinstance(self
.base
, QAPISchemaObjectType
)
977 self
.base
.check(schema
)
978 self
.base
.check_clash(schema
, seen
)
979 for m
in self
.local_members
:
982 self
.members
= seen
.values()
984 self
.variants
.check(schema
, seen
)
985 assert self
.variants
.tag_member
in self
.members
986 self
.variants
.check_clash(schema
, seen
)
988 def check_clash(self
, schema
, seen
):
989 assert not self
.variants
# not implemented
990 for m
in self
.members
:
993 def is_implicit(self
):
994 # See QAPISchema._make_implicit_object_type()
995 return self
.name
[0] == ':'
998 assert not self
.is_implicit()
999 return QAPISchemaType
.c_name(self
)
1001 def c_type(self
, is_param
=False):
1002 assert not self
.is_implicit()
1003 return QAPISchemaType
.c_type(self
)
1005 def json_type(self
):
1008 def visit(self
, visitor
):
1009 visitor
.visit_object_type(self
.name
, self
.info
,
1010 self
.base
, self
.local_members
, self
.variants
)
1011 visitor
.visit_object_type_flat(self
.name
, self
.info
,
1012 self
.members
, self
.variants
)
1015 class QAPISchemaObjectTypeMember(object):
1016 def __init__(self
, name
, typ
, optional
):
1017 assert isinstance(name
, str)
1018 assert isinstance(typ
, str)
1019 assert isinstance(optional
, bool)
1021 self
._type
_name
= typ
1023 self
.optional
= optional
1025 def check(self
, schema
):
1026 self
.type = schema
.lookup_type(self
._type
_name
)
1029 def check_clash(self
, seen
):
1030 # TODO change key of seen from QAPI name to C name
1031 assert self
.name
not in seen
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
, seen
):
1051 if not self
.tag_member
: # flat union
1052 self
.tag_member
= seen
[self
.tag_name
]
1053 assert isinstance(self
.tag_member
.type, QAPISchemaEnumType
)
1054 for v
in self
.variants
:
1056 assert v
.name
in self
.tag_member
.type.values
1057 if isinstance(v
.type, QAPISchemaObjectType
):
1058 v
.type.check(schema
)
1060 def check_clash(self
, schema
, seen
):
1061 for v
in self
.variants
:
1062 # Reset seen map for each variant, since qapi names from one
1063 # branch do not affect another branch
1064 assert isinstance(v
.type, QAPISchemaObjectType
)
1065 v
.type.check_clash(schema
, dict(seen
))
1068 class QAPISchemaObjectTypeVariant(QAPISchemaObjectTypeMember
):
1069 def __init__(self
, name
, typ
):
1070 QAPISchemaObjectTypeMember
.__init
__(self
, name
, typ
, False)
1072 # This function exists to support ugly simple union special cases
1073 # TODO get rid of them, and drop the function
1074 def simple_union_type(self
):
1075 if (self
.type.is_implicit() and
1076 isinstance(self
.type, QAPISchemaObjectType
)):
1077 assert len(self
.type.members
) == 1
1078 assert not self
.type.variants
1079 return self
.type.members
[0].type
1083 class QAPISchemaAlternateType(QAPISchemaType
):
1084 def __init__(self
, name
, info
, variants
):
1085 QAPISchemaType
.__init
__(self
, name
, info
)
1086 assert isinstance(variants
, QAPISchemaObjectTypeVariants
)
1087 assert not variants
.tag_name
1088 self
.variants
= variants
1090 def check(self
, schema
):
1091 self
.variants
.tag_member
.check(schema
)
1092 # Not calling self.variants.check_clash(), because there's nothing
1094 self
.variants
.check(schema
, {})
1096 def json_type(self
):
1099 def visit(self
, visitor
):
1100 visitor
.visit_alternate_type(self
.name
, self
.info
, self
.variants
)
1103 class QAPISchemaCommand(QAPISchemaEntity
):
1104 def __init__(self
, name
, info
, arg_type
, ret_type
, gen
, success_response
):
1105 QAPISchemaEntity
.__init
__(self
, name
, info
)
1106 assert not arg_type
or isinstance(arg_type
, str)
1107 assert not ret_type
or isinstance(ret_type
, str)
1108 self
._arg
_type
_name
= arg_type
1109 self
.arg_type
= None
1110 self
._ret
_type
_name
= ret_type
1111 self
.ret_type
= None
1113 self
.success_response
= success_response
1115 def check(self
, schema
):
1116 if self
._arg
_type
_name
:
1117 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1118 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1119 assert not self
.arg_type
.variants
# not implemented
1120 if self
._ret
_type
_name
:
1121 self
.ret_type
= schema
.lookup_type(self
._ret
_type
_name
)
1122 assert isinstance(self
.ret_type
, QAPISchemaType
)
1124 def visit(self
, visitor
):
1125 visitor
.visit_command(self
.name
, self
.info
,
1126 self
.arg_type
, self
.ret_type
,
1127 self
.gen
, self
.success_response
)
1130 class QAPISchemaEvent(QAPISchemaEntity
):
1131 def __init__(self
, name
, info
, arg_type
):
1132 QAPISchemaEntity
.__init
__(self
, name
, info
)
1133 assert not arg_type
or isinstance(arg_type
, str)
1134 self
._arg
_type
_name
= arg_type
1135 self
.arg_type
= None
1137 def check(self
, schema
):
1138 if self
._arg
_type
_name
:
1139 self
.arg_type
= schema
.lookup_type(self
._arg
_type
_name
)
1140 assert isinstance(self
.arg_type
, QAPISchemaObjectType
)
1141 assert not self
.arg_type
.variants
# not implemented
1143 def visit(self
, visitor
):
1144 visitor
.visit_event(self
.name
, self
.info
, self
.arg_type
)
1147 class QAPISchema(object):
1148 def __init__(self
, fname
):
1150 self
.exprs
= check_exprs(QAPISchemaParser(open(fname
, "r")).exprs
)
1151 self
._entity
_dict
= {}
1152 self
._predefining
= True
1153 self
._def
_predefineds
()
1154 self
._predefining
= False
1157 except (QAPISchemaError
, QAPIExprError
), err
:
1158 print >>sys
.stderr
, err
1161 def _def_entity(self
, ent
):
1162 # Only the predefined types are allowed to not have info
1163 assert ent
.info
or self
._predefining
1164 assert ent
.name
not in self
._entity
_dict
1165 self
._entity
_dict
[ent
.name
] = ent
1167 def lookup_entity(self
, name
, typ
=None):
1168 ent
= self
._entity
_dict
.get(name
)
1169 if typ
and not isinstance(ent
, typ
):
1173 def lookup_type(self
, name
):
1174 return self
.lookup_entity(name
, QAPISchemaType
)
1176 def _def_builtin_type(self
, name
, json_type
, c_type
, c_null
):
1177 self
._def
_entity
(QAPISchemaBuiltinType(name
, json_type
,
1179 # TODO As long as we have QAPI_TYPES_BUILTIN to share multiple
1180 # qapi-types.h from a single .c, all arrays of builtins must be
1181 # declared in the first file whether or not they are used. Nicer
1182 # would be to use lazy instantiation, while figuring out how to
1183 # avoid compilation issues with multiple qapi-types.h.
1184 self
._make
_array
_type
(name
, None)
1186 def _def_predefineds(self
):
1187 for t
in [('str', 'string', 'char' + pointer_suffix
, 'NULL'),
1188 ('number', 'number', 'double', '0'),
1189 ('int', 'int', 'int64_t', '0'),
1190 ('int8', 'int', 'int8_t', '0'),
1191 ('int16', 'int', 'int16_t', '0'),
1192 ('int32', 'int', 'int32_t', '0'),
1193 ('int64', 'int', 'int64_t', '0'),
1194 ('uint8', 'int', 'uint8_t', '0'),
1195 ('uint16', 'int', 'uint16_t', '0'),
1196 ('uint32', 'int', 'uint32_t', '0'),
1197 ('uint64', 'int', 'uint64_t', '0'),
1198 ('size', 'int', 'uint64_t', '0'),
1199 ('bool', 'boolean', 'bool', 'false'),
1200 ('any', 'value', 'QObject' + pointer_suffix
, 'NULL')]:
1201 self
._def
_builtin
_type
(*t
)
1202 self
.the_empty_object_type
= QAPISchemaObjectType(':empty', None, None,
1204 self
._def
_entity
(self
.the_empty_object_type
)
1206 def _make_implicit_enum_type(self
, name
, info
, values
):
1207 name
= name
+ 'Kind' # Use namespace reserved by add_name()
1208 self
._def
_entity
(QAPISchemaEnumType(name
, info
, values
, None))
1211 def _make_array_type(self
, element_type
, info
):
1212 name
= element_type
+ 'List' # Use namespace reserved by add_name()
1213 if not self
.lookup_type(name
):
1214 self
._def
_entity
(QAPISchemaArrayType(name
, info
, element_type
))
1217 def _make_implicit_object_type(self
, name
, info
, role
, members
):
1220 name
= ':obj-%s-%s' % (name
, role
)
1221 if not self
.lookup_entity(name
, QAPISchemaObjectType
):
1222 self
._def
_entity
(QAPISchemaObjectType(name
, info
, None,
1226 def _def_enum_type(self
, expr
, info
):
1229 prefix
= expr
.get('prefix')
1230 self
._def
_entity
(QAPISchemaEnumType(name
, info
, data
, prefix
))
1232 def _make_member(self
, name
, typ
, info
):
1234 if name
.startswith('*'):
1237 if isinstance(typ
, list):
1238 assert len(typ
) == 1
1239 typ
= self
._make
_array
_type
(typ
[0], info
)
1240 return QAPISchemaObjectTypeMember(name
, typ
, optional
)
1242 def _make_members(self
, data
, info
):
1243 return [self
._make
_member
(key
, value
, info
)
1244 for (key
, value
) in data
.iteritems()]
1246 def _def_struct_type(self
, expr
, info
):
1247 name
= expr
['struct']
1248 base
= expr
.get('base')
1250 self
._def
_entity
(QAPISchemaObjectType(name
, info
, base
,
1251 self
._make
_members
(data
, info
),
1254 def _make_variant(self
, case
, typ
):
1255 return QAPISchemaObjectTypeVariant(case
, typ
)
1257 def _make_simple_variant(self
, case
, typ
, info
):
1258 if isinstance(typ
, list):
1259 assert len(typ
) == 1
1260 typ
= self
._make
_array
_type
(typ
[0], info
)
1261 typ
= self
._make
_implicit
_object
_type
(
1262 typ
, info
, 'wrapper', [self
._make
_member
('data', typ
, info
)])
1263 return QAPISchemaObjectTypeVariant(case
, typ
)
1265 def _make_implicit_tag(self
, type_name
, info
, variants
):
1266 typ
= self
._make
_implicit
_enum
_type
(type_name
, info
,
1267 [v
.name
for v
in variants
])
1268 return QAPISchemaObjectTypeMember('type', typ
, False)
1270 def _def_union_type(self
, expr
, info
):
1271 name
= expr
['union']
1273 base
= expr
.get('base')
1274 tag_name
= expr
.get('discriminator')
1277 variants
= [self
._make
_variant
(key
, value
)
1278 for (key
, value
) in data
.iteritems()]
1281 variants
= [self
._make
_simple
_variant
(key
, value
, info
)
1282 for (key
, value
) in data
.iteritems()]
1283 tag_member
= self
._make
_implicit
_tag
(name
, info
, variants
)
1284 members
= [tag_member
]
1286 QAPISchemaObjectType(name
, info
, base
, members
,
1287 QAPISchemaObjectTypeVariants(tag_name
,
1291 def _def_alternate_type(self
, expr
, info
):
1292 name
= expr
['alternate']
1294 variants
= [self
._make
_variant
(key
, value
)
1295 for (key
, value
) in data
.iteritems()]
1296 tag_member
= self
._make
_implicit
_tag
(name
, info
, variants
)
1298 QAPISchemaAlternateType(name
, info
,
1299 QAPISchemaObjectTypeVariants(None,
1303 def _def_command(self
, expr
, info
):
1304 name
= expr
['command']
1305 data
= expr
.get('data')
1306 rets
= expr
.get('returns')
1307 gen
= expr
.get('gen', True)
1308 success_response
= expr
.get('success-response', True)
1309 if isinstance(data
, OrderedDict
):
1310 data
= self
._make
_implicit
_object
_type
(
1311 name
, info
, 'arg', self
._make
_members
(data
, info
))
1312 if isinstance(rets
, list):
1313 assert len(rets
) == 1
1314 rets
= self
._make
_array
_type
(rets
[0], info
)
1315 self
._def
_entity
(QAPISchemaCommand(name
, info
, data
, rets
, gen
,
1318 def _def_event(self
, expr
, info
):
1319 name
= expr
['event']
1320 data
= expr
.get('data')
1321 if isinstance(data
, OrderedDict
):
1322 data
= self
._make
_implicit
_object
_type
(
1323 name
, info
, 'arg', self
._make
_members
(data
, info
))
1324 self
._def
_entity
(QAPISchemaEvent(name
, info
, data
))
1326 def _def_exprs(self
):
1327 for expr_elem
in self
.exprs
:
1328 expr
= expr_elem
['expr']
1329 info
= expr_elem
['info']
1331 self
._def
_enum
_type
(expr
, info
)
1332 elif 'struct' in expr
:
1333 self
._def
_struct
_type
(expr
, info
)
1334 elif 'union' in expr
:
1335 self
._def
_union
_type
(expr
, info
)
1336 elif 'alternate' in expr
:
1337 self
._def
_alternate
_type
(expr
, info
)
1338 elif 'command' in expr
:
1339 self
._def
_command
(expr
, info
)
1340 elif 'event' in expr
:
1341 self
._def
_event
(expr
, info
)
1346 for ent
in self
._entity
_dict
.values():
1349 def visit(self
, visitor
):
1350 visitor
.visit_begin(self
)
1351 for (name
, entity
) in sorted(self
._entity
_dict
.items()):
1352 if visitor
.visit_needed(entity
):
1353 entity
.visit(visitor
)
1358 # Code generation helpers
1361 def camel_case(name
):
1365 if ch
in ['_', '-']:
1368 new_name
+= ch
.upper()
1371 new_name
+= ch
.lower()
1375 # ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
1376 # ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
1377 # ENUM24_Name -> ENUM24_NAME
1378 def camel_to_upper(value
):
1379 c_fun_str
= c_name(value
, False)
1387 # When c is upper and no "_" appears before, do more checks
1388 if c
.isupper() and (i
> 0) and c_fun_str
[i
- 1] != "_":
1389 if i
< l
- 1 and c_fun_str
[i
+ 1].islower():
1391 elif c_fun_str
[i
- 1].isdigit():
1394 return new_name
.lstrip('_').upper()
1397 def c_enum_const(type_name
, const_name
, prefix
=None):
1398 if prefix
is not None:
1400 return camel_to_upper(type_name
+ '_' + const_name
)
1402 c_name_trans
= string
.maketrans('.-', '__')
1405 # Map @name to a valid C identifier.
1406 # If @protect, avoid returning certain ticklish identifiers (like
1407 # C keywords) by prepending "q_".
1409 # Used for converting 'name' from a 'name':'type' qapi definition
1410 # into a generated struct member, as well as converting type names
1411 # into substrings of a generated C function name.
1412 # '__a.b_c' -> '__a_b_c', 'x-foo' -> 'x_foo'
1413 # protect=True: 'int' -> 'q_int'; protect=False: 'int' -> 'int'
1414 def c_name(name
, protect
=True):
1415 # ANSI X3J11/88-090, 3.1.1
1416 c89_words
= set(['auto', 'break', 'case', 'char', 'const', 'continue',
1417 'default', 'do', 'double', 'else', 'enum', 'extern',
1418 'float', 'for', 'goto', 'if', 'int', 'long', 'register',
1419 'return', 'short', 'signed', 'sizeof', 'static',
1420 'struct', 'switch', 'typedef', 'union', 'unsigned',
1421 'void', 'volatile', 'while'])
1422 # ISO/IEC 9899:1999, 6.4.1
1423 c99_words
= set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
1424 # ISO/IEC 9899:2011, 6.4.1
1425 c11_words
= set(['_Alignas', '_Alignof', '_Atomic', '_Generic',
1426 '_Noreturn', '_Static_assert', '_Thread_local'])
1427 # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
1429 gcc_words
= set(['asm', 'typeof'])
1430 # C++ ISO/IEC 14882:2003 2.11
1431 cpp_words
= set(['bool', 'catch', 'class', 'const_cast', 'delete',
1432 'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
1433 'namespace', 'new', 'operator', 'private', 'protected',
1434 'public', 'reinterpret_cast', 'static_cast', 'template',
1435 'this', 'throw', 'true', 'try', 'typeid', 'typename',
1436 'using', 'virtual', 'wchar_t',
1437 # alternative representations
1438 'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
1439 'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
1440 # namespace pollution:
1441 polluted_words
= set(['unix', 'errno'])
1442 if protect
and (name
in c89_words | c99_words | c11_words | gcc_words
1443 | cpp_words | polluted_words
):
1445 return name
.translate(c_name_trans
)
1447 eatspace
= '\033EATSPACE.'
1448 pointer_suffix
= ' *' + eatspace
1451 def genindent(count
):
1453 for _
in range(count
):
1460 def push_indent(indent_amount
=4):
1462 indent_level
+= indent_amount
1465 def pop_indent(indent_amount
=4):
1467 indent_level
-= indent_amount
1470 # Generate @code with @kwds interpolated.
1471 # Obey indent_level, and strip eatspace.
1472 def cgen(code
, **kwds
):
1475 indent
= genindent(indent_level
)
1476 # re.subn() lacks flags support before Python 2.7, use re.compile()
1477 raw
= re
.subn(re
.compile("^.", re
.MULTILINE
),
1478 indent
+ r
'\g<0>', raw
)
1480 return re
.sub(re
.escape(eatspace
) + ' *', '', raw
)
1483 def mcgen(code
, **kwds
):
1486 return cgen(code
, **kwds
)
1489 def guardname(filename
):
1490 return c_name(filename
, protect
=False).upper()
1493 def guardstart(name
):
1500 name
=guardname(name
))
1506 #endif /* %(name)s */
1509 name
=guardname(name
))
1512 def gen_enum_lookup(name
, values
, prefix
=None):
1515 const char *const %(c_name)s_lookup[] = {
1517 c_name
=c_name(name
))
1518 for value
in values
:
1519 index
= c_enum_const(name
, value
, prefix
)
1521 [%(index)s] = "%(value)s",
1523 index
=index
, value
=value
)
1525 max_index
= c_enum_const(name
, 'MAX', prefix
)
1527 [%(max_index)s] = NULL,
1530 max_index
=max_index
)
1534 def gen_enum(name
, values
, prefix
=None):
1535 # append automatically generated _MAX value
1536 enum_values
= values
+ ['MAX']
1540 typedef enum %(c_name)s {
1542 c_name
=c_name(name
))
1545 for value
in enum_values
:
1549 c_enum
=c_enum_const(name
, value
, prefix
),
1556 c_name
=c_name(name
))
1560 extern const char *const %(c_name)s_lookup[];
1562 c_name
=c_name(name
))
1566 def gen_params(arg_type
, extra
):
1569 assert not arg_type
.variants
1572 for memb
in arg_type
.members
:
1576 ret
+= 'bool has_%s, ' % c_name(memb
.name
)
1577 ret
+= '%s %s' % (memb
.type.c_type(is_param
=True), c_name(memb
.name
))
1583 def gen_err_check(label
='out', skiperr
=False):
1594 def gen_visit_fields(members
, prefix
='', need_cast
=False, skiperr
=False):
1601 for memb
in members
:
1604 visit_optional(v, &%(prefix)shas_%(c_name)s, "%(name)s", %(errp)s);
1606 prefix
=prefix
, c_name
=c_name(memb
.name
),
1607 name
=memb
.name
, errp
=errparg
)
1608 ret
+= gen_err_check(skiperr
=skiperr
)
1610 if (%(prefix)shas_%(c_name)s) {
1612 prefix
=prefix
, c_name
=c_name(memb
.name
))
1615 # Ugly: sometimes we need to cast away const
1616 if need_cast
and memb
.type.name
== 'str':
1622 visit_type_%(c_type)s(v, %(cast)s&%(prefix)s%(c_name)s, "%(name)s", %(errp)s);
1624 c_type
=memb
.type.c_name(), prefix
=prefix
, cast
=cast
,
1625 c_name
=c_name(memb
.name
), name
=memb
.name
,
1627 ret
+= gen_err_check(skiperr
=skiperr
)
1638 # Common command line parsing
1642 def parse_command_line(extra_options
="", extra_long_options
=[]):
1645 opts
, args
= getopt
.gnu_getopt(sys
.argv
[1:],
1646 "chp:o:" + extra_options
,
1647 ["source", "header", "prefix=",
1648 "output-dir="] + extra_long_options
)
1649 except getopt
.GetoptError
, err
:
1650 print >>sys
.stderr
, "%s: %s" % (sys
.argv
[0], str(err
))
1661 if o
in ("-p", "--prefix"):
1662 match
= re
.match('([A-Za-z_.-][A-Za-z0-9_.-]*)?', a
)
1663 if match
.end() != len(a
):
1664 print >>sys
.stderr
, \
1665 "%s: 'funny character '%s' in argument of --prefix" \
1666 % (sys
.argv
[0], a
[match
.end()])
1669 elif o
in ("-o", "--output-dir"):
1670 output_dir
= a
+ "/"
1671 elif o
in ("-c", "--source"):
1673 elif o
in ("-h", "--header"):
1676 extra_opts
.append(oa
)
1678 if not do_c
and not do_h
:
1683 print >>sys
.stderr
, "%s: need exactly one argument" % sys
.argv
[0]
1687 return (fname
, output_dir
, do_c
, do_h
, prefix
, extra_opts
)
1690 # Generate output files with boilerplate
1694 def open_output(output_dir
, do_c
, do_h
, prefix
, c_file
, h_file
,
1695 c_comment
, h_comment
):
1696 guard
= guardname(prefix
+ h_file
)
1697 c_file
= output_dir
+ prefix
+ c_file
1698 h_file
= output_dir
+ prefix
+ h_file
1702 os
.makedirs(output_dir
)
1704 if e
.errno
!= errno
.EEXIST
:
1707 def maybe_open(really
, name
, opt
):
1709 return open(name
, opt
)
1712 return StringIO
.StringIO()
1714 fdef
= maybe_open(do_c
, c_file
, 'w')
1715 fdecl
= maybe_open(do_h
, h_file
, 'w')
1717 fdef
.write(mcgen('''
1718 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1723 fdecl
.write(mcgen('''
1724 /* AUTOMATICALLY GENERATED, DO NOT MODIFY */
1730 comment
=h_comment
, guard
=guard
))
1732 return (fdef
, fdecl
)
1735 def close_output(fdef
, fdecl
):