qapi.py 33.9 KB
Newer Older
1 2 3 4
#
# QAPI helper library
#
# Copyright IBM, Corp. 2011
E
Eric Blake 已提交
5
# Copyright (c) 2013-2015 Red Hat Inc.
6 7 8
#
# Authors:
#  Anthony Liguori <aliguori@us.ibm.com>
9
#  Markus Armbruster <armbru@redhat.com>
10
#
11 12
# This work is licensed under the terms of the GNU GPL, version 2.
# See the COPYING file in the top-level directory.
13

14
import re
15
from ordereddict import OrderedDict
16
import os
17
import sys
18
import string
19

20
builtin_types = {
K
Kevin Wolf 已提交
21 22 23 24 25 26 27 28 29 30 31 32
    'str':      'QTYPE_QSTRING',
    'int':      'QTYPE_QINT',
    'number':   'QTYPE_QFLOAT',
    'bool':     'QTYPE_QBOOL',
    'int8':     'QTYPE_QINT',
    'int16':    'QTYPE_QINT',
    'int32':    'QTYPE_QINT',
    'int64':    'QTYPE_QINT',
    'uint8':    'QTYPE_QINT',
    'uint16':   'QTYPE_QINT',
    'uint32':   'QTYPE_QINT',
    'uint64':   'QTYPE_QINT',
33
    'size':     'QTYPE_QINT',
K
Kevin Wolf 已提交
34 35
}

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
# Whitelist of commands allowed to return a non-dictionary
returns_whitelist = [
    # From QMP:
    'human-monitor-command',
    'query-migrate-cache-size',
    'query-tpm-models',
    'query-tpm-types',
    'ringbuf-read',

    # From QGA:
    'guest-file-open',
    'guest-fsfreeze-freeze',
    'guest-fsfreeze-freeze-list',
    'guest-fsfreeze-status',
    'guest-fsfreeze-thaw',
    'guest-get-time',
    'guest-set-vcpus',
    'guest-sync',
    'guest-sync-delimited',

    # From qapi-schema-test:
    'user_def_cmd3',
]

60 61 62 63 64 65
enum_types = []
struct_types = []
union_types = []
events = []
all_names = {}

66 67 68 69 70 71 72 73
def error_path(parent):
    res = ""
    while parent:
        res = ("In file included from %s:%d:\n" % (parent['file'],
                                                   parent['line'])) + res
        parent = parent['parent']
    return res

74 75
class QAPISchemaError(Exception):
    def __init__(self, schema, msg):
76
        self.input_file = schema.input_file
77
        self.msg = msg
78 79 80 81
        self.col = 1
        self.line = schema.line
        for ch in schema.src[schema.line_pos:schema.pos]:
            if ch == '\t':
82 83 84
                self.col = (self.col + 7) % 8 + 1
            else:
                self.col += 1
85
        self.info = schema.parent_info
86 87

    def __str__(self):
88 89
        return error_path(self.info) + \
            "%s:%d:%d: %s" % (self.input_file, self.line, self.col, self.msg)
90

91 92
class QAPIExprError(Exception):
    def __init__(self, expr_info, msg):
93
        self.info = expr_info
94 95 96
        self.msg = msg

    def __str__(self):
97 98
        return error_path(self.info['parent']) + \
            "%s:%d: %s" % (self.info['file'], self.info['line'], self.msg)
99

100 101
class QAPISchema:

B
Benoît Canet 已提交
102 103 104 105 106
    def __init__(self, fp, input_relname=None, include_hist=[],
                 previously_included=[], parent_info=None):
        """ include_hist is a stack used to detect inclusion cycles
            previously_included is a global state used to avoid multiple
                                inclusions of the same file"""
107 108 109 110 111 112
        input_fname = os.path.abspath(fp.name)
        if input_relname is None:
            input_relname = fp.name
        self.input_dir = os.path.dirname(input_fname)
        self.input_file = input_relname
        self.include_hist = include_hist + [(input_relname, input_fname)]
B
Benoît Canet 已提交
113
        previously_included.append(input_fname)
114
        self.parent_info = parent_info
115 116 117 118
        self.src = fp.read()
        if self.src == '' or self.src[-1] != '\n':
            self.src += '\n'
        self.cursor = 0
119 120
        self.line = 1
        self.line_pos = 0
121 122 123 124
        self.exprs = []
        self.accept()

        while self.tok != None:
125 126 127 128 129 130 131 132 133 134 135
            expr_info = {'file': input_relname, 'line': self.line, 'parent': self.parent_info}
            expr = self.get_expr(False)
            if isinstance(expr, dict) and "include" in expr:
                if len(expr) != 1:
                    raise QAPIExprError(expr_info, "Invalid 'include' directive")
                include = expr["include"]
                if not isinstance(include, str):
                    raise QAPIExprError(expr_info,
                                        'Expected a file name (string), got: %s'
                                        % include)
                include_path = os.path.join(self.input_dir, include)
136 137 138 139
                for elem in self.include_hist:
                    if include_path == elem[1]:
                        raise QAPIExprError(expr_info, "Inclusion loop for %s"
                                            % include)
B
Benoît Canet 已提交
140 141 142
                # skip multiple include of the same file
                if include_path in previously_included:
                    continue
143 144
                try:
                    fobj = open(include_path, 'r')
145
                except IOError, e:
146 147
                    raise QAPIExprError(expr_info,
                                        '%s: %s' % (e.strerror, include))
B
Benoît Canet 已提交
148 149
                exprs_include = QAPISchema(fobj, include, self.include_hist,
                                           previously_included, expr_info)
150 151 152 153 154
                self.exprs.extend(exprs_include.exprs)
            else:
                expr_elem = {'expr': expr,
                             'info': expr_info}
                self.exprs.append(expr_elem)
155 156 157 158

    def accept(self):
        while True:
            self.tok = self.src[self.cursor]
159
            self.pos = self.cursor
160 161 162
            self.cursor += 1
            self.val = None

163
            if self.tok == '#':
164 165 166 167 168 169 170 171 172 173
                self.cursor = self.src.find('\n', self.cursor)
            elif self.tok in ['{', '}', ':', ',', '[', ']']:
                return
            elif self.tok == "'":
                string = ''
                esc = False
                while True:
                    ch = self.src[self.cursor]
                    self.cursor += 1
                    if ch == '\n':
174 175
                        raise QAPISchemaError(self,
                                              'Missing terminating "\'"')
176
                    if esc:
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211
                        if ch == 'b':
                            string += '\b'
                        elif ch == 'f':
                            string += '\f'
                        elif ch == 'n':
                            string += '\n'
                        elif ch == 'r':
                            string += '\r'
                        elif ch == 't':
                            string += '\t'
                        elif ch == 'u':
                            value = 0
                            for x in range(0, 4):
                                ch = self.src[self.cursor]
                                self.cursor += 1
                                if ch not in "0123456789abcdefABCDEF":
                                    raise QAPISchemaError(self,
                                                          '\\u escape needs 4 '
                                                          'hex digits')
                                value = (value << 4) + int(ch, 16)
                            # If Python 2 and 3 didn't disagree so much on
                            # how to handle Unicode, then we could allow
                            # Unicode string defaults.  But most of QAPI is
                            # ASCII-only, so we aren't losing much for now.
                            if not value or value > 0x7f:
                                raise QAPISchemaError(self,
                                                      'For now, \\u escape '
                                                      'only supports non-zero '
                                                      'values up to \\u007f')
                            string += chr(value)
                        elif ch in "\\/'\"":
                            string += ch
                        else:
                            raise QAPISchemaError(self,
                                                  "Unknown escape \\%s" %ch)
212 213 214 215 216 217 218 219
                        esc = False
                    elif ch == "\\":
                        esc = True
                    elif ch == "'":
                        self.val = string
                        return
                    else:
                        string += ch
220 221 222 223 224 225 226 227 228 229 230 231 232 233
            elif self.tok in "tfn":
                val = self.src[self.cursor - 1:]
                if val.startswith("true"):
                    self.val = True
                    self.cursor += 3
                    return
                elif val.startswith("false"):
                    self.val = False
                    self.cursor += 4
                    return
                elif val.startswith("null"):
                    self.val = None
                    self.cursor += 3
                    return
234 235 236 237
            elif self.tok == '\n':
                if self.cursor == len(self.src):
                    self.tok = None
                    return
238 239
                self.line += 1
                self.line_pos = self.cursor
240 241
            elif not self.tok.isspace():
                raise QAPISchemaError(self, 'Stray "%s"' % self.tok)
242 243 244

    def get_members(self):
        expr = OrderedDict()
245 246 247 248 249 250
        if self.tok == '}':
            self.accept()
            return expr
        if self.tok != "'":
            raise QAPISchemaError(self, 'Expected string or "}"')
        while True:
251 252
            key = self.val
            self.accept()
253 254 255
            if self.tok != ':':
                raise QAPISchemaError(self, 'Expected ":"')
            self.accept()
256 257
            if key in expr:
                raise QAPISchemaError(self, 'Duplicate key "%s"' % key)
258
            expr[key] = self.get_expr(True)
259
            if self.tok == '}':
260
                self.accept()
261 262 263 264 265 266
                return expr
            if self.tok != ',':
                raise QAPISchemaError(self, 'Expected "," or "}"')
            self.accept()
            if self.tok != "'":
                raise QAPISchemaError(self, 'Expected string')
267 268 269

    def get_values(self):
        expr = []
270 271 272
        if self.tok == ']':
            self.accept()
            return expr
273 274 275
        if not self.tok in "{['tfn":
            raise QAPISchemaError(self, 'Expected "{", "[", "]", string, '
                                  'boolean or "null"')
276
        while True:
277
            expr.append(self.get_expr(True))
278
            if self.tok == ']':
279
                self.accept()
280 281 282 283
                return expr
            if self.tok != ',':
                raise QAPISchemaError(self, 'Expected "," or "]"')
            self.accept()
284

285 286 287
    def get_expr(self, nested):
        if self.tok != '{' and not nested:
            raise QAPISchemaError(self, 'Expected "{"')
288 289 290 291 292 293
        if self.tok == '{':
            self.accept()
            expr = self.get_members()
        elif self.tok == '[':
            self.accept()
            expr = self.get_values()
294
        elif self.tok in "'tfn":
295 296
            expr = self.val
            self.accept()
297 298
        else:
            raise QAPISchemaError(self, 'Expected "{", "[" or string')
299
        return expr
K
Kevin Wolf 已提交
300

301 302 303 304 305 306
def find_base_fields(base):
    base_struct_define = find_struct(base)
    if not base_struct_define:
        return None
    return base_struct_define['data']

307 308
# Return the qtype of an alternate branch, or None on error.
def find_alternate_member_qtype(qapi_type):
E
Eric Blake 已提交
309 310 311 312 313 314
    if builtin_types.has_key(qapi_type):
        return builtin_types[qapi_type]
    elif find_struct(qapi_type):
        return "QTYPE_QDICT"
    elif find_enum(qapi_type):
        return "QTYPE_QSTRING"
315 316
    elif find_union(qapi_type):
        return "QTYPE_QDICT"
E
Eric Blake 已提交
317 318
    return None

319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
# Return the discriminator enum define if discriminator is specified as an
# enum type, otherwise return None.
def discriminator_find_enum_define(expr):
    base = expr.get('base')
    discriminator = expr.get('discriminator')

    if not (discriminator and base):
        return None

    base_fields = find_base_fields(base)
    if not base_fields:
        return None

    discriminator_type = base_fields.get(discriminator)
    if not discriminator_type:
        return None

    return find_enum(discriminator_type)

E
Eric Blake 已提交
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
valid_name = re.compile('^[a-zA-Z_][a-zA-Z0-9_.-]*$')
def check_name(expr_info, source, name, allow_optional = False,
               enum_member = False):
    global valid_name
    membername = name

    if not isinstance(name, str):
        raise QAPIExprError(expr_info,
                            "%s requires a string name" % source)
    if name.startswith('*'):
        membername = name[1:]
        if not allow_optional:
            raise QAPIExprError(expr_info,
                                "%s does not allow optional name '%s'"
                                % (source, name))
    # Enum members can start with a digit, because the generated C
    # code always prefixes it with the enum name
    if enum_member:
        membername = '_' + membername
    if not valid_name.match(membername):
        raise QAPIExprError(expr_info,
                            "%s uses invalid name '%s'" % (source, name))

361
def check_type(expr_info, source, value, allow_array = False,
362 363
               allow_dict = False, allow_optional = False,
               allow_star = False, allow_metas = []):
364 365 366 367 368 369
    global all_names
    orig_value = value

    if value is None:
        return

370
    if allow_star and value == '**':
371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
        return

    # Check if array type for value is okay
    if isinstance(value, list):
        if not allow_array:
            raise QAPIExprError(expr_info,
                                "%s cannot be an array" % source)
        if len(value) != 1 or not isinstance(value[0], str):
            raise QAPIExprError(expr_info,
                                "%s: array type must contain single type name"
                                % source)
        value = value[0]
        orig_value = "array of %s" %value

    # Check if type name for value is okay
    if isinstance(value, str):
387 388 389 390
        if value == '**':
            raise QAPIExprError(expr_info,
                                "%s uses '**' but did not request 'gen':false"
                                % source)
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408
        if not value in all_names:
            raise QAPIExprError(expr_info,
                                "%s uses unknown type '%s'"
                                % (source, orig_value))
        if not all_names[value] in allow_metas:
            raise QAPIExprError(expr_info,
                                "%s cannot use %s type '%s'"
                                % (source, all_names[value], orig_value))
        return

    # value is a dictionary, check that each member is okay
    if not isinstance(value, OrderedDict):
        raise QAPIExprError(expr_info,
                            "%s should be a dictionary" % source)
    if not allow_dict:
        raise QAPIExprError(expr_info,
                            "%s should be a type name" % source)
    for (key, arg) in value.items():
E
Eric Blake 已提交
409 410
        check_name(expr_info, "Member of %s" % source, key,
                   allow_optional=allow_optional)
411 412
        # Todo: allow dictionaries to represent default values of
        # an optional argument.
413
        check_type(expr_info, "Member '%s' of %s" % (key, source), arg,
414
                   allow_array=True, allow_star=allow_star,
415
                   allow_metas=['built-in', 'union', 'alternate', 'struct',
416
                                'enum'])
417

418 419 420 421 422 423 424 425 426 427 428 429 430 431
def check_member_clash(expr_info, base_name, data, source = ""):
    base = find_struct(base_name)
    assert base
    base_members = base['data']
    for key in data.keys():
        if key.startswith('*'):
            key = key[1:]
        if key in base_members or "*" + key in base_members:
            raise QAPIExprError(expr_info,
                                "Member name '%s'%s clashes with base '%s'"
                                % (key, source, base_name))
    if base.get('base'):
        check_member_clash(expr_info, base['base'], data, source)

432 433
def check_command(expr, expr_info):
    name = expr['command']
434 435
    allow_star = expr.has_key('gen')

436
    check_type(expr_info, "'data' for command '%s'" % name,
E
Eric Blake 已提交
437
               expr.get('data'), allow_dict=True, allow_optional=True,
438
               allow_metas=['union', 'struct'], allow_star=allow_star)
439 440 441
    returns_meta = ['union', 'struct']
    if name in returns_whitelist:
        returns_meta += ['built-in', 'alternate', 'enum']
442 443
    check_type(expr_info, "'returns' for command '%s'" % name,
               expr.get('returns'), allow_array=True, allow_dict=True,
444 445
               allow_optional=True, allow_metas=returns_meta,
               allow_star=allow_star)
446

W
Wenchao Xia 已提交
447
def check_event(expr, expr_info):
448 449
    global events
    name = expr['event']
W
Wenchao Xia 已提交
450
    params = expr.get('data')
451 452 453 454

    if name.upper() == 'MAX':
        raise QAPIExprError(expr_info, "Event name 'MAX' cannot be created")
    events.append(name)
455
    check_type(expr_info, "'data' for event '%s'" % name,
E
Eric Blake 已提交
456
               expr.get('data'), allow_dict=True, allow_optional=True,
457
               allow_metas=['union', 'struct'])
W
Wenchao Xia 已提交
458

459 460 461 462 463
def check_union(expr, expr_info):
    name = expr['union']
    base = expr.get('base')
    discriminator = expr.get('discriminator')
    members = expr['data']
E
Eric Blake 已提交
464
    values = { 'MAX': '(automatic)' }
465

466
    # If the object has a member 'base', its value must name a struct,
467 468 469 470 471 472
    # and there must be a discriminator.
    if base is not None:
        if discriminator is None:
            raise QAPIExprError(expr_info,
                                "Union '%s' requires a discriminator to go "
                                "along with base" %name)
473

474 475 476 477
    # Two types of unions, determined by discriminator.

    # With no discriminator it is a simple union.
    if discriminator is None:
478
        enum_define = None
479
        allow_metas=['built-in', 'union', 'alternate', 'struct', 'enum']
E
Eric Blake 已提交
480 481
        if base is not None:
            raise QAPIExprError(expr_info,
482
                                "Simple union '%s' must not have a base"
E
Eric Blake 已提交
483
                                % name)
484 485 486

    # Else, it's a flat union.
    else:
E
Eric Blake 已提交
487 488
        # The object must have a string member 'base'.
        if not isinstance(base, str):
489
            raise QAPIExprError(expr_info,
E
Eric Blake 已提交
490
                                "Flat union '%s' must have a string base field"
491
                                % name)
E
Eric Blake 已提交
492 493 494
        base_fields = find_base_fields(base)
        if not base_fields:
            raise QAPIExprError(expr_info,
495
                                "Base '%s' is not a valid struct"
E
Eric Blake 已提交
496 497
                                % base)

E
Eric Blake 已提交
498
        # The value of member 'discriminator' must name a non-optional
499
        # member of the base struct.
E
Eric Blake 已提交
500 501
        check_name(expr_info, "Discriminator of flat union '%s'" % name,
                   discriminator)
502 503 504 505
        discriminator_type = base_fields.get(discriminator)
        if not discriminator_type:
            raise QAPIExprError(expr_info,
                                "Discriminator '%s' is not a member of base "
506
                                "struct '%s'"
507 508
                                % (discriminator, base))
        enum_define = find_enum(discriminator_type)
509
        allow_metas=['struct']
510 511 512 513 514
        # Do not allow string discriminator
        if not enum_define:
            raise QAPIExprError(expr_info,
                                "Discriminator '%s' must be of enumeration "
                                "type" % discriminator)
515 516 517

    # Check every branch
    for (key, value) in members.items():
E
Eric Blake 已提交
518 519
        check_name(expr_info, "Member of union '%s'" % name, key)

520
        # Each value must name a known type; furthermore, in flat unions,
521
        # branches must be a struct with no overlapping member names
522 523
        check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
                   value, allow_array=True, allow_metas=allow_metas)
524 525 526 527 528
        if base:
            branch_struct = find_struct(value)
            assert branch_struct
            check_member_clash(expr_info, base, branch_struct['data'],
                               " of branch '%s'" % key)
529

E
Eric Blake 已提交
530
        # If the discriminator names an enum type, then all members
531
        # of 'data' must also be members of the enum type.
E
Eric Blake 已提交
532 533 534 535 536 537 538 539 540
        if enum_define:
            if not key in enum_define['enum_values']:
                raise QAPIExprError(expr_info,
                                    "Discriminator value '%s' is not found in "
                                    "enum '%s'" %
                                    (key, enum_define["enum_name"]))

        # Otherwise, check for conflicts in the generated enum
        else:
541
            c_key = camel_to_upper(key)
E
Eric Blake 已提交
542 543 544 545 546 547
            if c_key in values:
                raise QAPIExprError(expr_info,
                                    "Union '%s' member '%s' clashes with '%s'"
                                    % (name, key, values[c_key]))
            values[c_key] = key

548
def check_alternate(expr, expr_info):
549
    name = expr['alternate']
550 551 552 553 554 555
    members = expr['data']
    values = { 'MAX': '(automatic)' }
    types_seen = {}

    # Check every branch
    for (key, value) in members.items():
E
Eric Blake 已提交
556 557
        check_name(expr_info, "Member of alternate '%s'" % name, key)

558
        # Check for conflicts in the generated enum
559
        c_key = camel_to_upper(key)
560 561
        if c_key in values:
            raise QAPIExprError(expr_info,
562 563
                                "Alternate '%s' member '%s' clashes with '%s'"
                                % (name, key, values[c_key]))
564
        values[c_key] = key
E
Eric Blake 已提交
565

566
        # Ensure alternates have no type conflicts.
567 568 569
        check_type(expr_info, "Member '%s' of alternate '%s'" % (key, name),
                   value,
                   allow_metas=['built-in', 'union', 'struct', 'enum'])
570
        qtype = find_alternate_member_qtype(value)
571
        assert qtype
572 573
        if qtype in types_seen:
            raise QAPIExprError(expr_info,
574
                                "Alternate '%s' member '%s' can't "
575 576 577
                                "be distinguished from member '%s'"
                                % (name, key, types_seen[qtype]))
        types_seen[qtype] = key
578

579 580 581 582 583 584 585 586 587
def check_enum(expr, expr_info):
    name = expr['enum']
    members = expr.get('data')
    values = { 'MAX': '(automatic)' }

    if not isinstance(members, list):
        raise QAPIExprError(expr_info,
                            "Enum '%s' requires an array for 'data'" % name)
    for member in members:
E
Eric Blake 已提交
588 589
        check_name(expr_info, "Member of enum '%s'" %name, member,
                   enum_member=True)
590
        key = camel_to_upper(member)
591 592 593 594 595 596
        if key in values:
            raise QAPIExprError(expr_info,
                                "Enum '%s' member '%s' clashes with '%s'"
                                % (name, member, values[key]))
        values[key] = member

597
def check_struct(expr, expr_info):
598
    name = expr['struct']
599 600
    members = expr['data']

601
    check_type(expr_info, "'data' for struct '%s'" % name, members,
E
Eric Blake 已提交
602
               allow_dict=True, allow_optional=True)
603
    check_type(expr_info, "'base' for struct '%s'" % name, expr.get('base'),
604
               allow_metas=['struct'])
605 606
    if expr.get('base'):
        check_member_clash(expr_info, expr['base'], expr['data'])
607

608 609 610
def check_exprs(schema):
    for expr_elem in schema.exprs:
        expr = expr_elem['expr']
611 612 613 614 615
        info = expr_elem['info']

        if expr.has_key('enum'):
            check_enum(expr, info)
        elif expr.has_key('union'):
616 617 618
            check_union(expr, info)
        elif expr.has_key('alternate'):
            check_alternate(expr, info)
619
        elif expr.has_key('struct'):
620 621 622
            check_struct(expr, info)
        elif expr.has_key('command'):
            check_command(expr, info)
623 624
        elif expr.has_key('event'):
            check_event(expr, info)
625 626
        else:
            assert False, 'unexpected meta type'
627

628 629 630 631 632 633 634 635 636 637 638 639 640
def check_keys(expr_elem, meta, required, optional=[]):
    expr = expr_elem['expr']
    info = expr_elem['info']
    name = expr[meta]
    if not isinstance(name, str):
        raise QAPIExprError(info,
                            "'%s' key must have a string value" % meta)
    required = required + [ meta ]
    for (key, value) in expr.items():
        if not key in required and not key in optional:
            raise QAPIExprError(info,
                                "Unknown key '%s' in %s '%s'"
                                % (key, meta, name))
641 642 643 644
        if (key == 'gen' or key == 'success-response') and value != False:
            raise QAPIExprError(info,
                                "'%s' of %s '%s' should only use false value"
                                % (key, meta, name))
645 646 647 648 649 650 651
    for key in required:
        if not expr.has_key(key):
            raise QAPIExprError(info,
                                "Key '%s' is missing from %s '%s'"
                                % (key, meta, name))


652
def parse_schema(input_file):
653 654 655
    global all_names
    exprs = []

656
    # First pass: read entire file into memory
657
    try:
658
        schema = QAPISchema(open(input_file, "r"))
659
    except (QAPISchemaError, QAPIExprError), e:
660 661 662
        print >>sys.stderr, e
        exit(1)

663
    try:
664 665
        # Next pass: learn the types and check for valid expression keys. At
        # this point, top-level 'include' has already been flattened.
666 667
        for builtin in builtin_types.keys():
            all_names[builtin] = 'built-in'
668 669
        for expr_elem in schema.exprs:
            expr = expr_elem['expr']
670
            info = expr_elem['info']
671
            if expr.has_key('enum'):
672
                check_keys(expr_elem, 'enum', ['data'])
673
                add_enum(expr['enum'], info, expr['data'])
674
            elif expr.has_key('union'):
675 676
                check_keys(expr_elem, 'union', ['data'],
                           ['base', 'discriminator'])
677
                add_union(expr, info)
678 679
            elif expr.has_key('alternate'):
                check_keys(expr_elem, 'alternate', ['data'])
680
                add_name(expr['alternate'], info, 'alternate')
681 682
            elif expr.has_key('struct'):
                check_keys(expr_elem, 'struct', ['data'], ['base'])
683
                add_struct(expr, info)
684 685 686
            elif expr.has_key('command'):
                check_keys(expr_elem, 'command', [],
                           ['data', 'returns', 'gen', 'success-response'])
687
                add_name(expr['command'], info, 'command')
688 689
            elif expr.has_key('event'):
                check_keys(expr_elem, 'event', [], ['data'])
690
                add_name(expr['event'], info, 'event')
691 692 693
            else:
                raise QAPIExprError(expr_elem['info'],
                                    "Expression is missing metatype")
694 695 696 697 698 699 700
            exprs.append(expr)

        # Try again for hidden UnionKind enum
        for expr_elem in schema.exprs:
            expr = expr_elem['expr']
            if expr.has_key('union'):
                if not discriminator_find_enum_define(expr):
701 702
                    add_enum('%sKind' % expr['union'], expr_elem['info'],
                             implicit=True)
703
            elif expr.has_key('alternate'):
704 705
                add_enum('%sKind' % expr['alternate'], expr_elem['info'],
                         implicit=True)
706 707

        # Final pass - validate that exprs make sense
708 709 710 711 712
        check_exprs(schema)
    except QAPIExprError, e:
        print >>sys.stderr, e
        exit(1)

713 714 715
    return exprs

def parse_args(typeinfo):
E
Eric Blake 已提交
716
    if isinstance(typeinfo, str):
717 718 719 720
        struct = find_struct(typeinfo)
        assert struct != None
        typeinfo = struct['data']

721 722 723 724 725 726 727
    for member in typeinfo:
        argname = member
        argentry = typeinfo[member]
        optional = False
        if member.startswith('*'):
            argname = member[1:]
            optional = True
728 729 730
        # Todo: allow argentry to be OrderedDict, for providing the
        # value of an optional argument.
        yield (argname, argentry, optional)
731 732 733 734 735 736 737 738 739 740 741 742 743 744

def camel_case(name):
    new_name = ''
    first = True
    for ch in name:
        if ch in ['_', '-']:
            first = True
        elif first:
            new_name += ch.upper()
            first = False
        else:
            new_name += ch.lower()
    return new_name

745
c_name_trans = string.maketrans('.-', '__')
746

747
def c_name(name, protect=True):
B
Blue Swirl 已提交
748 749 750 751 752 753 754 755 756 757 758 759 760 761
    # ANSI X3J11/88-090, 3.1.1
    c89_words = set(['auto', 'break', 'case', 'char', 'const', 'continue',
                     'default', 'do', 'double', 'else', 'enum', 'extern', 'float',
                     'for', 'goto', 'if', 'int', 'long', 'register', 'return',
                     'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
                     'typedef', 'union', 'unsigned', 'void', 'volatile', 'while'])
    # ISO/IEC 9899:1999, 6.4.1
    c99_words = set(['inline', 'restrict', '_Bool', '_Complex', '_Imaginary'])
    # ISO/IEC 9899:2011, 6.4.1
    c11_words = set(['_Alignas', '_Alignof', '_Atomic', '_Generic', '_Noreturn',
                     '_Static_assert', '_Thread_local'])
    # GCC http://gcc.gnu.org/onlinedocs/gcc-4.7.1/gcc/C-Extensions.html
    # excluding _.*
    gcc_words = set(['asm', 'typeof'])
762 763 764 765 766 767 768 769 770 771
    # C++ ISO/IEC 14882:2003 2.11
    cpp_words = set(['bool', 'catch', 'class', 'const_cast', 'delete',
                     'dynamic_cast', 'explicit', 'false', 'friend', 'mutable',
                     'namespace', 'new', 'operator', 'private', 'protected',
                     'public', 'reinterpret_cast', 'static_cast', 'template',
                     'this', 'throw', 'true', 'try', 'typeid', 'typename',
                     'using', 'virtual', 'wchar_t',
                     # alternative representations
                     'and', 'and_eq', 'bitand', 'bitor', 'compl', 'not',
                     'not_eq', 'or', 'or_eq', 'xor', 'xor_eq'])
772
    # namespace pollution:
773
    polluted_words = set(['unix', 'errno'])
774
    if protect and (name in c89_words | c99_words | c11_words | gcc_words | cpp_words | polluted_words):
B
Blue Swirl 已提交
775
        return "q_" + name
776
    return name.translate(c_name_trans)
777 778 779 780 781 782 783 784 785

def c_list_type(name):
    return '%sList' % name

def type_name(name):
    if type(name) == list:
        return c_list_type(name[0])
    return name

786
def add_name(name, info, meta, implicit = False):
787
    global all_names
788
    check_name(info, "'%s'" % meta, name)
789 790 791 792 793 794 795 796 797
    if name in all_names:
        raise QAPIExprError(info,
                            "%s '%s' is already defined"
                            % (all_names[name], name))
    if not implicit and name[-4:] == 'Kind':
        raise QAPIExprError(info,
                            "%s '%s' should not end in 'Kind'"
                            % (meta, name))
    all_names[name] = meta
798

799
def add_struct(definition, info):
800
    global struct_types
801 802
    name = definition['struct']
    add_name(name, info, 'struct')
803 804 805 806 807
    struct_types.append(definition)

def find_struct(name):
    global struct_types
    for struct in struct_types:
808
        if struct['struct'] == name:
809 810
            return struct
    return None
811

812
def add_union(definition, info):
813
    global union_types
814 815
    name = definition['union']
    add_name(name, info, 'union')
816
    union_types.append(definition)
817 818 819 820 821 822 823 824

def find_union(name):
    global union_types
    for union in union_types:
        if union['union'] == name:
            return union
    return None

825
def add_enum(name, info, enum_values = None, implicit = False):
826
    global enum_types
827
    add_name(name, info, 'enum', implicit)
828
    enum_types.append({"enum_name": name, "enum_values": enum_values})
829

830
def find_enum(name):
831
    global enum_types
832 833 834 835 836 837 838
    for enum in enum_types:
        if enum['enum_name'] == name:
            return enum
    return None

def is_enum(name):
    return find_enum(name) != None
839

840 841 842 843 844
eatspace = '\033EATSPACE.'

# A special suffix is added in c_type() for pointer types, and it's
# stripped in mcgen(). So please notice this when you check the return
# value of c_type() outside mcgen().
845
def c_type(name, is_param=False):
846
    if name == 'str':
847
        if is_param:
848 849 850
            return 'const char *' + eatspace
        return 'char *' + eatspace

851 852
    elif name == 'int':
        return 'int64_t'
853 854 855 856
    elif (name == 'int8' or name == 'int16' or name == 'int32' or
          name == 'int64' or name == 'uint8' or name == 'uint16' or
          name == 'uint32' or name == 'uint64'):
        return name + '_t'
L
Laszlo Ersek 已提交
857 858
    elif name == 'size':
        return 'uint64_t'
859 860 861 862 863
    elif name == 'bool':
        return 'bool'
    elif name == 'number':
        return 'double'
    elif type(name) == list:
864
        return '%s *%s' % (c_list_type(name[0]), eatspace)
865 866 867 868
    elif is_enum(name):
        return name
    elif name == None or len(name) == 0:
        return 'void'
869
    elif name in events:
870
        return '%sEvent *%s' % (camel_case(name), eatspace)
871
    else:
872 873 874 875 876
        return '%s *%s' % (name, eatspace)

def is_c_ptr(name):
    suffix = "*" + eatspace
    return c_type(name).endswith(suffix)
877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900

def genindent(count):
    ret = ""
    for i in range(count):
        ret += " "
    return ret

indent_level = 0

def push_indent(indent_amount=4):
    global indent_level
    indent_level += indent_amount

def pop_indent(indent_amount=4):
    global indent_level
    indent_level -= indent_amount

def cgen(code, **kwds):
    indent = genindent(indent_level)
    lines = code.split('\n')
    lines = map(lambda x: indent + x, lines)
    return '\n'.join(lines) % kwds + '\n'

def mcgen(code, **kwds):
901 902
    raw = cgen('\n'.join(code.split('\n')[1:-1]), **kwds)
    return re.sub(re.escape(eatspace) + ' *', '', raw)
903 904 905 906 907

def basename(filename):
    return filename.split("/")[-1]

def guardname(filename):
M
Michael Roth 已提交
908 909 910 911
    guard = basename(filename).rsplit(".", 1)[0]
    for substr in [".", " ", "-"]:
        guard = guard.replace(substr, "_")
    return guard.upper() + '_H'
912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928

def guardstart(name):
    return mcgen('''

#ifndef %(name)s
#define %(name)s

''',
                 name=guardname(name))

def guardend(name):
    return mcgen('''

#endif /* %(name)s */

''',
                 name=guardname(name))
929

930 931 932
# ENUMName -> ENUM_NAME, EnumName1 -> ENUM_NAME1
# ENUM_NAME -> ENUM_NAME, ENUM_NAME1 -> ENUM_NAME1, ENUM_Name2 -> ENUM_NAME2
# ENUM24_Name -> ENUM24_NAME
933
def camel_to_upper(value):
934
    c_fun_str = c_name(value, False)
935
    if value.isupper():
936 937
        return c_fun_str

938
    new_name = ''
939 940 941 942 943 944 945 946 947 948
    l = len(c_fun_str)
    for i in range(l):
        c = c_fun_str[i]
        # When c is upper and no "_" appears before, do more checks
        if c.isupper() and (i > 0) and c_fun_str[i - 1] != "_":
            # Case 1: next string is lower
            # Case 2: previous string is digit
            if (i < (l - 1) and c_fun_str[i + 1].islower()) or \
            c_fun_str[i - 1].isdigit():
                new_name += '_'
949 950
        new_name += c
    return new_name.lstrip('_').upper()
951

952
def c_enum_const(type_name, const_name):
953
    return camel_to_upper(type_name + '_' + const_name)