xpcom/idl-parser/xpidl.py

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 #!/usr/bin/env python
michael@0 2 # xpidl.py - A parser for cross-platform IDL (XPIDL) files.
michael@0 3 #
michael@0 4 # This Source Code Form is subject to the terms of the Mozilla Public
michael@0 5 # License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 6 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
michael@0 7
michael@0 8 """A parser for cross-platform IDL (XPIDL) files."""
michael@0 9
michael@0 10 import sys, os.path, re
michael@0 11 from ply import lex, yacc
michael@0 12
michael@0 13 """A type conforms to the following pattern:
michael@0 14
michael@0 15 def isScriptable(self):
michael@0 16 'returns True or False'
michael@0 17
michael@0 18 def nativeType(self, calltype):
michael@0 19 'returns a string representation of the native type
michael@0 20 calltype must be 'in', 'out', or 'inout'
michael@0 21
michael@0 22 Interface members const/method/attribute conform to the following pattern:
michael@0 23
michael@0 24 name = 'string'
michael@0 25
michael@0 26 def toIDL(self):
michael@0 27 'returns the member signature as IDL'
michael@0 28 """
michael@0 29
michael@0 30 def attlistToIDL(attlist):
michael@0 31 if len(attlist) == 0:
michael@0 32 return ''
michael@0 33
michael@0 34 attlist = list(attlist)
michael@0 35 attlist.sort(cmp=lambda a,b: cmp(a[0], b[0]))
michael@0 36
michael@0 37 return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
michael@0 38 for name, value, aloc in attlist])
michael@0 39
michael@0 40 _paramsHardcode = {
michael@0 41 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
michael@0 42 3: ('array', 'size_is', 'const'),
michael@0 43 }
michael@0 44
michael@0 45 def paramAttlistToIDL(attlist):
michael@0 46 if len(attlist) == 0:
michael@0 47 return ''
michael@0 48
michael@0 49 # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
michael@0 50 # quirk
michael@0 51 attlist = list(attlist)
michael@0 52 sorted = []
michael@0 53 if len(attlist) in _paramsHardcode:
michael@0 54 for p in _paramsHardcode[len(attlist)]:
michael@0 55 i = 0
michael@0 56 while i < len(attlist):
michael@0 57 if attlist[i][0] == p:
michael@0 58 sorted.append(attlist[i])
michael@0 59 del attlist[i]
michael@0 60 continue
michael@0 61
michael@0 62 i += 1
michael@0 63
michael@0 64 sorted.extend(attlist)
michael@0 65
michael@0 66 return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
michael@0 67 for name, value, aloc in sorted])
michael@0 68
michael@0 69 def unaliasType(t):
michael@0 70 while t.kind == 'typedef':
michael@0 71 t = t.realtype
michael@0 72 assert t is not None
michael@0 73 return t
michael@0 74
michael@0 75 def getBuiltinOrNativeTypeName(t):
michael@0 76 t = unaliasType(t)
michael@0 77 if t.kind == 'builtin':
michael@0 78 return t.name
michael@0 79 elif t.kind == 'native':
michael@0 80 assert t.specialtype is not None
michael@0 81 return '[%s]' % t.specialtype
michael@0 82 else:
michael@0 83 return None
michael@0 84
michael@0 85 class BuiltinLocation(object):
michael@0 86 def get(self):
michael@0 87 return "<builtin type>"
michael@0 88
michael@0 89 def __str__(self):
michael@0 90 return self.get()
michael@0 91
michael@0 92 class Builtin(object):
michael@0 93 kind = 'builtin'
michael@0 94 location = BuiltinLocation
michael@0 95
michael@0 96 def __init__(self, name, nativename, signed=False, maybeConst=False):
michael@0 97 self.name = name
michael@0 98 self.nativename = nativename
michael@0 99 self.signed = signed
michael@0 100 self.maybeConst = maybeConst
michael@0 101
michael@0 102 def isScriptable(self):
michael@0 103 return True
michael@0 104
michael@0 105 def nativeType(self, calltype, shared=False, const=False):
michael@0 106 if const:
michael@0 107 print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True)
michael@0 108 const = 'const '
michael@0 109 elif calltype == 'in' and self.nativename.endswith('*'):
michael@0 110 const = 'const '
michael@0 111 elif shared:
michael@0 112 if not self.nativename.endswith('*'):
michael@0 113 raise IDLError("[shared] not applicable to non-pointer types.", self.location)
michael@0 114 const = 'const '
michael@0 115 else:
michael@0 116 const = ''
michael@0 117 return "%s%s %s" % (const, self.nativename,
michael@0 118 calltype != 'in' and '*' or '')
michael@0 119
michael@0 120 builtinNames = [
michael@0 121 Builtin('boolean', 'bool'),
michael@0 122 Builtin('void', 'void'),
michael@0 123 Builtin('octet', 'uint8_t'),
michael@0 124 Builtin('short', 'int16_t', True, True),
michael@0 125 Builtin('long', 'int32_t', True, True),
michael@0 126 Builtin('long long', 'int64_t', True, False),
michael@0 127 Builtin('unsigned short', 'uint16_t', False, True),
michael@0 128 Builtin('unsigned long', 'uint32_t', False, True),
michael@0 129 Builtin('unsigned long long', 'uint64_t', False, False),
michael@0 130 Builtin('float', 'float', True, False),
michael@0 131 Builtin('double', 'double', True, False),
michael@0 132 Builtin('char', 'char', True, False),
michael@0 133 Builtin('string', 'char *', False, False),
michael@0 134 Builtin('wchar', 'char16_t', False, False),
michael@0 135 Builtin('wstring', 'char16_t *', False, False),
michael@0 136 ]
michael@0 137
michael@0 138 builtinMap = {}
michael@0 139 for b in builtinNames:
michael@0 140 builtinMap[b.name] = b
michael@0 141
michael@0 142 class Location(object):
michael@0 143 _line = None
michael@0 144
michael@0 145 def __init__(self, lexer, lineno, lexpos):
michael@0 146 self._lineno = lineno
michael@0 147 self._lexpos = lexpos
michael@0 148 self._lexdata = lexer.lexdata
michael@0 149 self._file = getattr(lexer, 'filename', "<unknown>")
michael@0 150
michael@0 151 def __eq__(self, other):
michael@0 152 return self._lexpos == other._lexpos and \
michael@0 153 self._file == other._file
michael@0 154
michael@0 155 def resolve(self):
michael@0 156 if self._line:
michael@0 157 return
michael@0 158
michael@0 159 startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
michael@0 160 endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
michael@0 161 self._line = self._lexdata[startofline:endofline]
michael@0 162 self._colno = self._lexpos - startofline
michael@0 163
michael@0 164 def pointerline(self):
michael@0 165 def i():
michael@0 166 for i in xrange(0, self._colno):
michael@0 167 yield " "
michael@0 168 yield "^"
michael@0 169
michael@0 170 return "".join(i())
michael@0 171
michael@0 172 def get(self):
michael@0 173 self.resolve()
michael@0 174 return "%s line %s:%s" % (self._file, self._lineno, self._colno)
michael@0 175
michael@0 176 def __str__(self):
michael@0 177 self.resolve()
michael@0 178 return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
michael@0 179 self._line, self.pointerline())
michael@0 180
michael@0 181 class NameMap(object):
michael@0 182 """Map of name -> object. Each object must have a .name and .location property.
michael@0 183 Setting the same name twice throws an error."""
michael@0 184 def __init__(self):
michael@0 185 self._d = {}
michael@0 186
michael@0 187 def __getitem__(self, key):
michael@0 188 if key in builtinMap:
michael@0 189 return builtinMap[key]
michael@0 190 return self._d[key]
michael@0 191
michael@0 192 def __iter__(self):
michael@0 193 return self._d.itervalues()
michael@0 194
michael@0 195 def __contains__(self, key):
michael@0 196 return key in builtinMap or key in self._d
michael@0 197
michael@0 198 def set(self, object):
michael@0 199 if object.name in builtinMap:
michael@0 200 raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location)
michael@0 201 if object.name.startswith("_"):
michael@0 202 object.name = object.name[1:]
michael@0 203 if object.name in self._d:
michael@0 204 old = self._d[object.name]
michael@0 205 if old == object: return
michael@0 206 if isinstance(old, Forward) and isinstance(object, Interface):
michael@0 207 self._d[object.name] = object
michael@0 208 elif isinstance(old, Interface) and isinstance(object, Forward):
michael@0 209 pass
michael@0 210 else:
michael@0 211 raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location)
michael@0 212 else:
michael@0 213 self._d[object.name] = object
michael@0 214
michael@0 215 def get(self, id, location):
michael@0 216 try:
michael@0 217 return self[id]
michael@0 218 except KeyError:
michael@0 219 raise IDLError("Name '%s' not found", location)
michael@0 220
michael@0 221 class IDLError(Exception):
michael@0 222 def __init__(self, message, location, warning=False):
michael@0 223 self.message = message
michael@0 224 self.location = location
michael@0 225 self.warning = warning
michael@0 226
michael@0 227 def __str__(self):
michael@0 228 return "%s: %s, %s" % (self.warning and 'warning' or 'error',
michael@0 229 self.message, self.location)
michael@0 230
michael@0 231 class Include(object):
michael@0 232 kind = 'include'
michael@0 233
michael@0 234 def __init__(self, filename, location):
michael@0 235 self.filename = filename
michael@0 236 self.location = location
michael@0 237
michael@0 238 def __str__(self):
michael@0 239 return "".join(["include '%s'\n" % self.filename])
michael@0 240
michael@0 241 def resolve(self, parent):
michael@0 242 def incfiles():
michael@0 243 yield self.filename
michael@0 244 for dir in parent.incdirs:
michael@0 245 yield os.path.join(dir, self.filename)
michael@0 246
michael@0 247 for file in incfiles():
michael@0 248 if not os.path.exists(file): continue
michael@0 249
michael@0 250 self.IDL = parent.parser.parse(open(file).read(), filename=file)
michael@0 251 self.IDL.resolve(parent.incdirs, parent.parser)
michael@0 252 for type in self.IDL.getNames():
michael@0 253 parent.setName(type)
michael@0 254 parent.deps.extend(self.IDL.deps)
michael@0 255 return
michael@0 256
michael@0 257 raise IDLError("File '%s' not found" % self.filename, self.location)
michael@0 258
michael@0 259 class IDL(object):
michael@0 260 def __init__(self, productions):
michael@0 261 self.productions = productions
michael@0 262 self.deps = []
michael@0 263
michael@0 264 def setName(self, object):
michael@0 265 self.namemap.set(object)
michael@0 266
michael@0 267 def getName(self, id, location):
michael@0 268 try:
michael@0 269 return self.namemap[id]
michael@0 270 except KeyError:
michael@0 271 raise IDLError("type '%s' not found" % id, location)
michael@0 272
michael@0 273 def hasName(self, id):
michael@0 274 return id in self.namemap
michael@0 275
michael@0 276 def getNames(self):
michael@0 277 return iter(self.namemap)
michael@0 278
michael@0 279 def __str__(self):
michael@0 280 return "".join([str(p) for p in self.productions])
michael@0 281
michael@0 282 def resolve(self, incdirs, parser):
michael@0 283 self.namemap = NameMap()
michael@0 284 self.incdirs = incdirs
michael@0 285 self.parser = parser
michael@0 286 for p in self.productions:
michael@0 287 p.resolve(self)
michael@0 288
michael@0 289 def includes(self):
michael@0 290 for p in self.productions:
michael@0 291 if p.kind == 'include':
michael@0 292 yield p
michael@0 293
michael@0 294 def needsJSTypes(self):
michael@0 295 for p in self.productions:
michael@0 296 if p.kind == 'interface' and p.needsJSTypes():
michael@0 297 return True
michael@0 298 return False
michael@0 299
michael@0 300 class CDATA(object):
michael@0 301 kind = 'cdata'
michael@0 302 _re = re.compile(r'\n+')
michael@0 303
michael@0 304 def __init__(self, data, location):
michael@0 305 self.data = self._re.sub('\n', data)
michael@0 306 self.location = location
michael@0 307
michael@0 308 def resolve(self, parent):
michael@0 309 pass
michael@0 310
michael@0 311 def __str__(self):
michael@0 312 return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
michael@0 313
michael@0 314 def count(self):
michael@0 315 return 0
michael@0 316
michael@0 317 class Typedef(object):
michael@0 318 kind = 'typedef'
michael@0 319
michael@0 320 def __init__(self, type, name, location, doccomments):
michael@0 321 self.type = type
michael@0 322 self.name = name
michael@0 323 self.location = location
michael@0 324 self.doccomments = doccomments
michael@0 325
michael@0 326 def __eq__(self, other):
michael@0 327 return self.name == other.name and self.type == other.type
michael@0 328
michael@0 329 def resolve(self, parent):
michael@0 330 parent.setName(self)
michael@0 331 self.realtype = parent.getName(self.type, self.location)
michael@0 332
michael@0 333 def isScriptable(self):
michael@0 334 return self.realtype.isScriptable()
michael@0 335
michael@0 336 def nativeType(self, calltype):
michael@0 337 return "%s %s" % (self.name,
michael@0 338 calltype != 'in' and '*' or '')
michael@0 339
michael@0 340 def __str__(self):
michael@0 341 return "typedef %s %s\n" % (self.type, self.name)
michael@0 342
michael@0 343 class Forward(object):
michael@0 344 kind = 'forward'
michael@0 345
michael@0 346 def __init__(self, name, location, doccomments):
michael@0 347 self.name = name
michael@0 348 self.location = location
michael@0 349 self.doccomments = doccomments
michael@0 350
michael@0 351 def __eq__(self, other):
michael@0 352 return other.kind == 'forward' and other.name == self.name
michael@0 353
michael@0 354 def resolve(self, parent):
michael@0 355 # Hack alert: if an identifier is already present, move the doccomments
michael@0 356 # forward.
michael@0 357 if parent.hasName(self.name):
michael@0 358 for i in xrange(0, len(parent.productions)):
michael@0 359 if parent.productions[i] is self: break
michael@0 360 for i in xrange(i + 1, len(parent.productions)):
michael@0 361 if hasattr(parent.productions[i], 'doccomments'):
michael@0 362 parent.productions[i].doccomments[0:0] = self.doccomments
michael@0 363 break
michael@0 364
michael@0 365 parent.setName(self)
michael@0 366
michael@0 367 def isScriptable(self):
michael@0 368 return True
michael@0 369
michael@0 370 def nativeType(self, calltype):
michael@0 371 return "%s %s" % (self.name,
michael@0 372 calltype != 'in' and '* *' or '*')
michael@0 373
michael@0 374 def __str__(self):
michael@0 375 return "forward-declared %s\n" % self.name
michael@0 376
michael@0 377 class Native(object):
michael@0 378 kind = 'native'
michael@0 379
michael@0 380 modifier = None
michael@0 381 specialtype = None
michael@0 382
michael@0 383 specialtypes = {
michael@0 384 'nsid': None,
michael@0 385 'domstring': 'nsAString',
michael@0 386 'utf8string': 'nsACString',
michael@0 387 'cstring': 'nsACString',
michael@0 388 'astring': 'nsAString',
michael@0 389 'jsval': 'JS::Value'
michael@0 390 }
michael@0 391
michael@0 392 def __init__(self, name, nativename, attlist, location):
michael@0 393 self.name = name
michael@0 394 self.nativename = nativename
michael@0 395 self.location = location
michael@0 396
michael@0 397 for name, value, aloc in attlist:
michael@0 398 if value is not None:
michael@0 399 raise IDLError("Unexpected attribute value", aloc)
michael@0 400 if name in ('ptr', 'ref'):
michael@0 401 if self.modifier is not None:
michael@0 402 raise IDLError("More than one ptr/ref modifier", aloc)
michael@0 403 self.modifier = name
michael@0 404 elif name in self.specialtypes.keys():
michael@0 405 if self.specialtype is not None:
michael@0 406 raise IDLError("More than one special type", aloc)
michael@0 407 self.specialtype = name
michael@0 408 if self.specialtypes[name] is not None:
michael@0 409 self.nativename = self.specialtypes[name]
michael@0 410 else:
michael@0 411 raise IDLError("Unexpected attribute", aloc)
michael@0 412
michael@0 413 def __eq__(self, other):
michael@0 414 return self.name == other.name and \
michael@0 415 self.nativename == other.nativename and \
michael@0 416 self.modifier == other.modifier and \
michael@0 417 self.specialtype == other.specialtype
michael@0 418
michael@0 419 def resolve(self, parent):
michael@0 420 parent.setName(self)
michael@0 421
michael@0 422 def isScriptable(self):
michael@0 423 if self.specialtype is None:
michael@0 424 return False
michael@0 425
michael@0 426 if self.specialtype == 'nsid':
michael@0 427 return self.modifier is not None
michael@0 428
michael@0 429 return self.modifier == 'ref'
michael@0 430
michael@0 431 def isPtr(self, calltype):
michael@0 432 return self.modifier == 'ptr'
michael@0 433
michael@0 434 def isRef(self, calltype):
michael@0 435 return self.modifier == 'ref'
michael@0 436
michael@0 437 def nativeType(self, calltype, const=False, shared=False):
michael@0 438 if shared:
michael@0 439 if calltype != 'out':
michael@0 440 raise IDLError("[shared] only applies to out parameters.")
michael@0 441 const = True
michael@0 442
michael@0 443 if self.specialtype is not None and calltype == 'in':
michael@0 444 const = True
michael@0 445
michael@0 446 if self.specialtype == 'jsval':
michael@0 447 if calltype == 'out' or calltype == 'inout':
michael@0 448 return "JS::MutableHandleValue "
michael@0 449 return "JS::HandleValue "
michael@0 450
michael@0 451 if self.isRef(calltype):
michael@0 452 m = '& '
michael@0 453 elif self.isPtr(calltype):
michael@0 454 m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
michael@0 455 else:
michael@0 456 m = calltype != 'in' and '*' or ''
michael@0 457 return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
michael@0 458
michael@0 459 def __str__(self):
michael@0 460 return "native %s(%s)\n" % (self.name, self.nativename)
michael@0 461
michael@0 462 class Interface(object):
michael@0 463 kind = 'interface'
michael@0 464
michael@0 465 def __init__(self, name, attlist, base, members, location, doccomments):
michael@0 466 self.name = name
michael@0 467 self.attributes = InterfaceAttributes(attlist, location)
michael@0 468 self.base = base
michael@0 469 self.members = members
michael@0 470 self.location = location
michael@0 471 self.namemap = NameMap()
michael@0 472 self.doccomments = doccomments
michael@0 473 self.nativename = name
michael@0 474
michael@0 475 for m in members:
michael@0 476 if not isinstance(m, CDATA):
michael@0 477 self.namemap.set(m)
michael@0 478
michael@0 479 def __eq__(self, other):
michael@0 480 return self.name == other.name and self.location == other.location
michael@0 481
michael@0 482 def resolve(self, parent):
michael@0 483 self.idl = parent
michael@0 484
michael@0 485 # Hack alert: if an identifier is already present, libIDL assigns
michael@0 486 # doc comments incorrectly. This is quirks-mode extraordinaire!
michael@0 487 if parent.hasName(self.name):
michael@0 488 for member in self.members:
michael@0 489 if hasattr(member, 'doccomments'):
michael@0 490 member.doccomments[0:0] = self.doccomments
michael@0 491 break
michael@0 492 self.doccomments = parent.getName(self.name, None).doccomments
michael@0 493
michael@0 494 if self.attributes.function:
michael@0 495 has_method = False
michael@0 496 for member in self.members:
michael@0 497 if member.kind is 'method':
michael@0 498 if has_method:
michael@0 499 raise IDLError("interface '%s' has multiple methods, but marked 'function'" % self.name, self.location)
michael@0 500 else:
michael@0 501 has_method = True
michael@0 502
michael@0 503 parent.setName(self)
michael@0 504 if self.base is not None:
michael@0 505 realbase = parent.getName(self.base, self.location)
michael@0 506 if realbase.kind != 'interface':
michael@0 507 raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location)
michael@0 508
michael@0 509 if self.attributes.scriptable and not realbase.attributes.scriptable:
michael@0 510 print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
michael@0 511
michael@0 512 if self.attributes.scriptable and realbase.attributes.builtinclass and not self.attributes.builtinclass:
michael@0 513 raise IDLError("interface '%s' is not builtinclass but derives from builtinclass '%s'" % (self.name, self.base), self.location)
michael@0 514
michael@0 515 for member in self.members:
michael@0 516 member.resolve(self)
michael@0 517
michael@0 518 # The number 250 is NOT arbitrary; this number is the maximum number of
michael@0 519 # stub entries defined in xpcom/reflect/xptcall/public/genstubs.pl
michael@0 520 # Do not increase this value without increasing the number in that
michael@0 521 # location, or you WILL cause otherwise unknown problems!
michael@0 522 if self.countEntries() > 250 and not self.attributes.builtinclass:
michael@0 523 raise IDLError("interface '%s' has too many entries" % self.name,
michael@0 524 self.location)
michael@0 525
michael@0 526 def isScriptable(self):
michael@0 527 # NOTE: this is not whether *this* interface is scriptable... it's
michael@0 528 # whether, when used as a type, it's scriptable, which is true of all
michael@0 529 # interfaces.
michael@0 530 return True
michael@0 531
michael@0 532 def nativeType(self, calltype, const=False):
michael@0 533 return "%s%s %s" % (const and 'const ' or '',
michael@0 534 self.name,
michael@0 535 calltype != 'in' and '* *' or '*')
michael@0 536
michael@0 537 def __str__(self):
michael@0 538 l = ["interface %s\n" % self.name]
michael@0 539 if self.base is not None:
michael@0 540 l.append("\tbase %s\n" % self.base)
michael@0 541 l.append(str(self.attributes))
michael@0 542 if self.members is None:
michael@0 543 l.append("\tincomplete type\n")
michael@0 544 else:
michael@0 545 for m in self.members:
michael@0 546 l.append(str(m))
michael@0 547 return "".join(l)
michael@0 548
michael@0 549 def getConst(self, name, location):
michael@0 550 # The constant may be in a base class
michael@0 551 iface = self
michael@0 552 while name not in iface.namemap and iface is not None:
michael@0 553 iface = self.idl.getName(self.base, self.location)
michael@0 554 if iface is None:
michael@0 555 raise IDLError("cannot find symbol '%s'" % name, c.location)
michael@0 556 c = iface.namemap.get(name, location)
michael@0 557 if c.kind != 'const':
michael@0 558 raise IDLError("symbol '%s' is not a constant", c.location)
michael@0 559
michael@0 560 return c.getValue()
michael@0 561
michael@0 562 def needsJSTypes(self):
michael@0 563 for m in self.members:
michael@0 564 if m.kind == "attribute" and m.type == "jsval":
michael@0 565 return True
michael@0 566 if m.kind == "method" and m.needsJSTypes():
michael@0 567 return True
michael@0 568 return False
michael@0 569
michael@0 570 def countEntries(self):
michael@0 571 ''' Returns the number of entries in the vtable for this interface. '''
michael@0 572 total = sum(member.count() for member in self.members)
michael@0 573 if self.base is not None:
michael@0 574 realbase = self.idl.getName(self.base, self.location)
michael@0 575 total += realbase.countEntries()
michael@0 576 return total
michael@0 577
michael@0 578 class InterfaceAttributes(object):
michael@0 579 uuid = None
michael@0 580 scriptable = False
michael@0 581 builtinclass = False
michael@0 582 function = False
michael@0 583 deprecated = False
michael@0 584 noscript = False
michael@0 585
michael@0 586 def setuuid(self, value):
michael@0 587 self.uuid = value.lower()
michael@0 588
michael@0 589 def setscriptable(self):
michael@0 590 self.scriptable = True
michael@0 591
michael@0 592 def setfunction(self):
michael@0 593 self.function = True
michael@0 594
michael@0 595 def setnoscript(self):
michael@0 596 self.noscript = True
michael@0 597
michael@0 598 def setbuiltinclass(self):
michael@0 599 self.builtinclass = True
michael@0 600
michael@0 601 def setdeprecated(self):
michael@0 602 self.deprecated = True
michael@0 603
michael@0 604 actions = {
michael@0 605 'uuid': (True, setuuid),
michael@0 606 'scriptable': (False, setscriptable),
michael@0 607 'builtinclass': (False, setbuiltinclass),
michael@0 608 'function': (False, setfunction),
michael@0 609 'noscript': (False, setnoscript),
michael@0 610 'deprecated': (False, setdeprecated),
michael@0 611 'object': (False, lambda self: True),
michael@0 612 }
michael@0 613
michael@0 614 def __init__(self, attlist, location):
michael@0 615 def badattribute(self):
michael@0 616 raise IDLError("Unexpected interface attribute '%s'" % name, location)
michael@0 617
michael@0 618 for name, val, aloc in attlist:
michael@0 619 hasval, action = self.actions.get(name, (False, badattribute))
michael@0 620 if hasval:
michael@0 621 if val is None:
michael@0 622 raise IDLError("Expected value for attribute '%s'" % name,
michael@0 623 aloc)
michael@0 624
michael@0 625 action(self, val)
michael@0 626 else:
michael@0 627 if val is not None:
michael@0 628 raise IDLError("Unexpected value for attribute '%s'" % name,
michael@0 629 aloc)
michael@0 630
michael@0 631 action(self)
michael@0 632
michael@0 633 if self.uuid is None:
michael@0 634 raise IDLError("interface has no uuid", location)
michael@0 635
michael@0 636 def __str__(self):
michael@0 637 l = []
michael@0 638 if self.uuid:
michael@0 639 l.append("\tuuid: %s\n" % self.uuid)
michael@0 640 if self.scriptable:
michael@0 641 l.append("\tscriptable\n")
michael@0 642 if self.builtinclass:
michael@0 643 l.append("\tbuiltinclass\n")
michael@0 644 if self.function:
michael@0 645 l.append("\tfunction\n")
michael@0 646 return "".join(l)
michael@0 647
michael@0 648 class ConstMember(object):
michael@0 649 kind = 'const'
michael@0 650 def __init__(self, type, name, value, location, doccomments):
michael@0 651 self.type = type
michael@0 652 self.name = name
michael@0 653 self.value = value
michael@0 654 self.location = location
michael@0 655 self.doccomments = doccomments
michael@0 656
michael@0 657 def resolve(self, parent):
michael@0 658 self.realtype = parent.idl.getName(self.type, self.location)
michael@0 659 self.iface = parent
michael@0 660 basetype = self.realtype
michael@0 661 while isinstance(basetype, Typedef):
michael@0 662 basetype = basetype.realtype
michael@0 663 if not isinstance(basetype, Builtin) or not basetype.maybeConst:
michael@0 664 raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
michael@0 665
michael@0 666 self.basetype = basetype
michael@0 667
michael@0 668 def getValue(self):
michael@0 669 return self.value(self.iface)
michael@0 670
michael@0 671 def __str__(self):
michael@0 672 return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
michael@0 673
michael@0 674 def count(self):
michael@0 675 return 0
michael@0 676
michael@0 677 class Attribute(object):
michael@0 678 kind = 'attribute'
michael@0 679 noscript = False
michael@0 680 readonly = False
michael@0 681 implicit_jscontext = False
michael@0 682 nostdcall = False
michael@0 683 binaryname = None
michael@0 684 null = None
michael@0 685 undefined = None
michael@0 686 deprecated = False
michael@0 687 infallible = False
michael@0 688
michael@0 689 def __init__(self, type, name, attlist, readonly, location, doccomments):
michael@0 690 self.type = type
michael@0 691 self.name = name
michael@0 692 self.attlist = attlist
michael@0 693 self.readonly = readonly
michael@0 694 self.location = location
michael@0 695 self.doccomments = doccomments
michael@0 696
michael@0 697 for name, value, aloc in attlist:
michael@0 698 if name == 'binaryname':
michael@0 699 if value is None:
michael@0 700 raise IDLError("binaryname attribute requires a value",
michael@0 701 aloc)
michael@0 702
michael@0 703 self.binaryname = value
michael@0 704 continue
michael@0 705
michael@0 706 if name == 'Null':
michael@0 707 if value is None:
michael@0 708 raise IDLError("'Null' attribute requires a value", aloc)
michael@0 709 if readonly:
michael@0 710 raise IDLError("'Null' attribute only makes sense for setters",
michael@0 711 aloc);
michael@0 712 if value not in ('Empty', 'Null', 'Stringify'):
michael@0 713 raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'",
michael@0 714 aloc);
michael@0 715 self.null = value
michael@0 716 elif name == 'Undefined':
michael@0 717 if value is None:
michael@0 718 raise IDLError("'Undefined' attribute requires a value", aloc)
michael@0 719 if readonly:
michael@0 720 raise IDLError("'Undefined' attribute only makes sense for setters",
michael@0 721 aloc);
michael@0 722 if value not in ('Empty', 'Null'):
michael@0 723 raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'",
michael@0 724 aloc);
michael@0 725 self.undefined = value
michael@0 726 else:
michael@0 727 if value is not None:
michael@0 728 raise IDLError("Unexpected attribute value", aloc)
michael@0 729
michael@0 730 if name == 'noscript':
michael@0 731 self.noscript = True
michael@0 732 elif name == 'implicit_jscontext':
michael@0 733 self.implicit_jscontext = True
michael@0 734 elif name == 'deprecated':
michael@0 735 self.deprecated = True
michael@0 736 elif name == 'nostdcall':
michael@0 737 self.nostdcall = True
michael@0 738 elif name == 'infallible':
michael@0 739 self.infallible = True
michael@0 740 else:
michael@0 741 raise IDLError("Unexpected attribute '%s'" % name, aloc)
michael@0 742
michael@0 743 def resolve(self, iface):
michael@0 744 self.iface = iface
michael@0 745 self.realtype = iface.idl.getName(self.type, self.location)
michael@0 746 if (self.null is not None and
michael@0 747 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
michael@0 748 raise IDLError("'Null' attribute can only be used on DOMString",
michael@0 749 self.location)
michael@0 750 if (self.undefined is not None and
michael@0 751 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
michael@0 752 raise IDLError("'Undefined' attribute can only be used on DOMString",
michael@0 753 self.location)
michael@0 754 if self.infallible and not self.realtype.kind == 'builtin':
michael@0 755 raise IDLError('[infallible] only works on builtin types '
michael@0 756 '(numbers, booleans, and raw char types)',
michael@0 757 self.location)
michael@0 758 if self.infallible and not iface.attributes.builtinclass:
michael@0 759 raise IDLError('[infallible] attributes are only allowed on '
michael@0 760 '[builtinclass] interfaces',
michael@0 761 self.location)
michael@0 762
michael@0 763
michael@0 764 def toIDL(self):
michael@0 765 attribs = attlistToIDL(self.attlist)
michael@0 766 readonly = self.readonly and 'readonly ' or ''
michael@0 767 return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
michael@0 768
michael@0 769 def isScriptable(self):
michael@0 770 if not self.iface.attributes.scriptable: return False
michael@0 771 return not self.noscript
michael@0 772
michael@0 773 def __str__(self):
michael@0 774 return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
michael@0 775 self.type, self.name)
michael@0 776
michael@0 777 def count(self):
michael@0 778 return self.readonly and 1 or 2
michael@0 779
michael@0 780 class Method(object):
michael@0 781 kind = 'method'
michael@0 782 noscript = False
michael@0 783 notxpcom = False
michael@0 784 binaryname = None
michael@0 785 implicit_jscontext = False
michael@0 786 nostdcall = False
michael@0 787 optional_argc = False
michael@0 788 deprecated = False
michael@0 789
michael@0 790 def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
michael@0 791 self.type = type
michael@0 792 self.name = name
michael@0 793 self.attlist = attlist
michael@0 794 self.params = paramlist
michael@0 795 self.location = location
michael@0 796 self.doccomments = doccomments
michael@0 797 self.raises = raises
michael@0 798
michael@0 799 for name, value, aloc in attlist:
michael@0 800 if name == 'binaryname':
michael@0 801 if value is None:
michael@0 802 raise IDLError("binaryname attribute requires a value",
michael@0 803 aloc)
michael@0 804
michael@0 805 self.binaryname = value
michael@0 806 continue
michael@0 807
michael@0 808 if value is not None:
michael@0 809 raise IDLError("Unexpected attribute value", aloc)
michael@0 810
michael@0 811 if name == 'noscript':
michael@0 812 self.noscript = True
michael@0 813 elif name == 'notxpcom':
michael@0 814 self.notxpcom = True
michael@0 815 elif name == 'implicit_jscontext':
michael@0 816 self.implicit_jscontext = True
michael@0 817 elif name == 'optional_argc':
michael@0 818 self.optional_argc = True
michael@0 819 elif name == 'deprecated':
michael@0 820 self.deprecated = True
michael@0 821 elif name == 'nostdcall':
michael@0 822 self.nostdcall = True
michael@0 823 else:
michael@0 824 raise IDLError("Unexpected attribute '%s'" % name, aloc)
michael@0 825
michael@0 826 self.namemap = NameMap()
michael@0 827 for p in paramlist:
michael@0 828 self.namemap.set(p)
michael@0 829
michael@0 830 def resolve(self, iface):
michael@0 831 self.iface = iface
michael@0 832 self.realtype = self.iface.idl.getName(self.type, self.location)
michael@0 833 for p in self.params:
michael@0 834 p.resolve(self)
michael@0 835 for p in self.params:
michael@0 836 if p.retval and p != self.params[-1]:
michael@0 837 raise IDLError("'retval' parameter '%s' is not the last parameter" % p.name, self.location)
michael@0 838 if p.size_is:
michael@0 839 found_size_param = False
michael@0 840 for size_param in self.params:
michael@0 841 if p.size_is == size_param.name:
michael@0 842 found_size_param = True
michael@0 843 if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long':
michael@0 844 raise IDLError("is_size parameter must have type 'unsigned long'", self.location)
michael@0 845 if not found_size_param:
michael@0 846 raise IDLError("could not find is_size parameter '%s'" % p.size_is, self.location)
michael@0 847
michael@0 848 def isScriptable(self):
michael@0 849 if not self.iface.attributes.scriptable: return False
michael@0 850 return not (self.noscript or self.notxpcom)
michael@0 851
michael@0 852 def __str__(self):
michael@0 853 return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
michael@0 854
michael@0 855 def toIDL(self):
michael@0 856 if len(self.raises):
michael@0 857 raises = ' raises (%s)' % ','.join(self.raises)
michael@0 858 else:
michael@0 859 raises = ''
michael@0 860
michael@0 861 return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
michael@0 862 self.type,
michael@0 863 self.name,
michael@0 864 ", ".join([p.toIDL()
michael@0 865 for p in self.params]),
michael@0 866 raises)
michael@0 867
michael@0 868 def needsJSTypes(self):
michael@0 869 if self.implicit_jscontext:
michael@0 870 return True
michael@0 871 if self.type == "jsval":
michael@0 872 return True
michael@0 873 for p in self.params:
michael@0 874 t = p.realtype
michael@0 875 if isinstance(t, Native) and t.specialtype == "jsval":
michael@0 876 return True
michael@0 877 return False
michael@0 878
michael@0 879 def count(self):
michael@0 880 return 1
michael@0 881
michael@0 882 class Param(object):
michael@0 883 size_is = None
michael@0 884 iid_is = None
michael@0 885 const = False
michael@0 886 array = False
michael@0 887 retval = False
michael@0 888 shared = False
michael@0 889 optional = False
michael@0 890 null = None
michael@0 891 undefined = None
michael@0 892
michael@0 893 def __init__(self, paramtype, type, name, attlist, location, realtype=None):
michael@0 894 self.paramtype = paramtype
michael@0 895 self.type = type
michael@0 896 self.name = name
michael@0 897 self.attlist = attlist
michael@0 898 self.location = location
michael@0 899 self.realtype = realtype
michael@0 900
michael@0 901 for name, value, aloc in attlist:
michael@0 902 # Put the value-taking attributes first!
michael@0 903 if name == 'size_is':
michael@0 904 if value is None:
michael@0 905 raise IDLError("'size_is' must specify a parameter", aloc)
michael@0 906 self.size_is = value
michael@0 907 elif name == 'iid_is':
michael@0 908 if value is None:
michael@0 909 raise IDLError("'iid_is' must specify a parameter", aloc)
michael@0 910 self.iid_is = value
michael@0 911 elif name == 'Null':
michael@0 912 if value is None:
michael@0 913 raise IDLError("'Null' must specify a parameter", aloc)
michael@0 914 if value not in ('Empty', 'Null', 'Stringify'):
michael@0 915 raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'",
michael@0 916 aloc);
michael@0 917 self.null = value
michael@0 918 elif name == 'Undefined':
michael@0 919 if value is None:
michael@0 920 raise IDLError("'Undefined' must specify a parameter", aloc)
michael@0 921 if value not in ('Empty', 'Null'):
michael@0 922 raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'",
michael@0 923 aloc);
michael@0 924 self.undefined = value
michael@0 925 else:
michael@0 926 if value is not None:
michael@0 927 raise IDLError("Unexpected value for attribute '%s'" % name,
michael@0 928 aloc)
michael@0 929
michael@0 930 if name == 'const':
michael@0 931 self.const = True
michael@0 932 elif name == 'array':
michael@0 933 self.array = True
michael@0 934 elif name == 'retval':
michael@0 935 self.retval = True
michael@0 936 elif name == 'shared':
michael@0 937 self.shared = True
michael@0 938 elif name == 'optional':
michael@0 939 self.optional = True
michael@0 940 else:
michael@0 941 raise IDLError("Unexpected attribute '%s'" % name, aloc)
michael@0 942
michael@0 943 def resolve(self, method):
michael@0 944 self.realtype = method.iface.idl.getName(self.type, self.location)
michael@0 945 if self.array:
michael@0 946 self.realtype = Array(self.realtype)
michael@0 947 if (self.null is not None and
michael@0 948 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
michael@0 949 raise IDLError("'Null' attribute can only be used on DOMString",
michael@0 950 self.location)
michael@0 951 if (self.undefined is not None and
michael@0 952 getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
michael@0 953 raise IDLError("'Undefined' attribute can only be used on DOMString",
michael@0 954 self.location)
michael@0 955
michael@0 956 def nativeType(self):
michael@0 957 kwargs = {}
michael@0 958 if self.shared: kwargs['shared'] = True
michael@0 959 if self.const: kwargs['const'] = True
michael@0 960
michael@0 961 try:
michael@0 962 return self.realtype.nativeType(self.paramtype, **kwargs)
michael@0 963 except IDLError, e:
michael@0 964 raise IDLError(e.message, self.location)
michael@0 965 except TypeError, e:
michael@0 966 raise IDLError("Unexpected parameter attribute", self.location)
michael@0 967
michael@0 968 def toIDL(self):
michael@0 969 return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
michael@0 970 self.paramtype,
michael@0 971 self.type,
michael@0 972 self.name)
michael@0 973
michael@0 974 class Array(object):
michael@0 975 def __init__(self, basetype):
michael@0 976 self.type = basetype
michael@0 977
michael@0 978 def isScriptable(self):
michael@0 979 return self.type.isScriptable()
michael@0 980
michael@0 981 def nativeType(self, calltype, const=False):
michael@0 982 return "%s%s*" % (const and 'const ' or '',
michael@0 983 self.type.nativeType(calltype))
michael@0 984
michael@0 985 class IDLParser(object):
michael@0 986 keywords = {
michael@0 987 'const': 'CONST',
michael@0 988 'interface': 'INTERFACE',
michael@0 989 'in': 'IN',
michael@0 990 'inout': 'INOUT',
michael@0 991 'out': 'OUT',
michael@0 992 'attribute': 'ATTRIBUTE',
michael@0 993 'raises': 'RAISES',
michael@0 994 'readonly': 'READONLY',
michael@0 995 'native': 'NATIVE',
michael@0 996 'typedef': 'TYPEDEF',
michael@0 997 'Infinity': 'INFINITY'
michael@0 998 }
michael@0 999
michael@0 1000 tokens = [
michael@0 1001 'IDENTIFIER',
michael@0 1002 'CDATA',
michael@0 1003 'INCLUDE',
michael@0 1004 'IID',
michael@0 1005 'NUMBER',
michael@0 1006 'HEXNUM',
michael@0 1007 'LSHIFT',
michael@0 1008 'RSHIFT',
michael@0 1009 'NATIVEID',
michael@0 1010 ]
michael@0 1011
michael@0 1012 tokens.extend(keywords.values())
michael@0 1013
michael@0 1014 states = (
michael@0 1015 ('nativeid', 'exclusive'),
michael@0 1016 )
michael@0 1017
michael@0 1018 hexchar = r'[a-fA-F0-9]'
michael@0 1019
michael@0 1020 t_NUMBER = r'-?\d+'
michael@0 1021 t_HEXNUM = r'0x%s+' % hexchar
michael@0 1022 t_LSHIFT = r'<<'
michael@0 1023 t_RSHIFT= r'>>'
michael@0 1024
michael@0 1025 literals = '"(){}[],;:=|+-*'
michael@0 1026
michael@0 1027 t_ignore = ' \t'
michael@0 1028
michael@0 1029 def t_multilinecomment(self, t):
michael@0 1030 r'/\*(?s).*?\*/'
michael@0 1031 t.lexer.lineno += t.value.count('\n')
michael@0 1032 if t.value.startswith("/**"):
michael@0 1033 self._doccomments.append(t.value)
michael@0 1034
michael@0 1035 def t_singlelinecomment(self, t):
michael@0 1036 r'(?m)//.*?$'
michael@0 1037
michael@0 1038 def t_IID(self, t):
michael@0 1039 return t
michael@0 1040 t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
michael@0 1041
michael@0 1042 def t_IDENTIFIER(self, t):
michael@0 1043 r'(unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long)(?!_?[A-Za-z][A-Za-z_0-9])|_?[A-Za-z][A-Za-z_0-9]*'
michael@0 1044 t.type = self.keywords.get(t.value, 'IDENTIFIER')
michael@0 1045 return t
michael@0 1046
michael@0 1047 def t_LCDATA(self, t):
michael@0 1048 r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
michael@0 1049 t.type = 'CDATA'
michael@0 1050 t.value = t.lexer.lexmatch.group('cdata')
michael@0 1051 t.lexer.lineno += t.value.count('\n')
michael@0 1052 return t
michael@0 1053
michael@0 1054 def t_INCLUDE(self, t):
michael@0 1055 r'\#include[ \t]+"[^"\n]+"'
michael@0 1056 inc, value, end = t.value.split('"')
michael@0 1057 t.value = value
michael@0 1058 return t
michael@0 1059
michael@0 1060 def t_directive(self, t):
michael@0 1061 r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
michael@0 1062 raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
michael@0 1063 Location(lexer=self.lexer, lineno=self.lexer.lineno,
michael@0 1064 lexpos=self.lexer.lexpos))
michael@0 1065
michael@0 1066 def t_newline(self, t):
michael@0 1067 r'\n+'
michael@0 1068 t.lexer.lineno += len(t.value)
michael@0 1069
michael@0 1070 def t_nativeid_NATIVEID(self, t):
michael@0 1071 r'[^()\n]+(?=\))'
michael@0 1072 t.lexer.begin('INITIAL')
michael@0 1073 return t
michael@0 1074
michael@0 1075 t_nativeid_ignore = ''
michael@0 1076
michael@0 1077 def t_ANY_error(self, t):
michael@0 1078 raise IDLError("unrecognized input",
michael@0 1079 Location(lexer=self.lexer,
michael@0 1080 lineno=self.lexer.lineno,
michael@0 1081 lexpos=self.lexer.lexpos))
michael@0 1082
michael@0 1083 precedence = (
michael@0 1084 ('left', '|'),
michael@0 1085 ('left', 'LSHIFT', 'RSHIFT'),
michael@0 1086 ('left', '+', '-'),
michael@0 1087 ('left', '*'),
michael@0 1088 ('left', 'UMINUS'),
michael@0 1089 )
michael@0 1090
michael@0 1091 def p_idlfile(self, p):
michael@0 1092 """idlfile : productions"""
michael@0 1093 p[0] = IDL(p[1])
michael@0 1094
michael@0 1095 def p_productions_start(self, p):
michael@0 1096 """productions : """
michael@0 1097 p[0] = []
michael@0 1098
michael@0 1099 def p_productions_cdata(self, p):
michael@0 1100 """productions : CDATA productions"""
michael@0 1101 p[0] = list(p[2])
michael@0 1102 p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
michael@0 1103
michael@0 1104 def p_productions_include(self, p):
michael@0 1105 """productions : INCLUDE productions"""
michael@0 1106 p[0] = list(p[2])
michael@0 1107 p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
michael@0 1108
michael@0 1109 def p_productions_interface(self, p):
michael@0 1110 """productions : interface productions
michael@0 1111 | typedef productions
michael@0 1112 | native productions"""
michael@0 1113 p[0] = list(p[2])
michael@0 1114 p[0].insert(0, p[1])
michael@0 1115
michael@0 1116 def p_typedef(self, p):
michael@0 1117 """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
michael@0 1118 p[0] = Typedef(type=p[2],
michael@0 1119 name=p[3],
michael@0 1120 location=self.getLocation(p, 1),
michael@0 1121 doccomments=p.slice[1].doccomments)
michael@0 1122
michael@0 1123 def p_native(self, p):
michael@0 1124 """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
michael@0 1125 p[0] = Native(name=p[3],
michael@0 1126 nativename=p[6],
michael@0 1127 attlist=p[1]['attlist'],
michael@0 1128 location=self.getLocation(p, 2))
michael@0 1129
michael@0 1130 def p_afternativeid(self, p):
michael@0 1131 """afternativeid : """
michael@0 1132 # this is a place marker: we switch the lexer into literal identifier
michael@0 1133 # mode here, to slurp up everything until the closeparen
michael@0 1134 self.lexer.begin('nativeid')
michael@0 1135
michael@0 1136 def p_anyident(self, p):
michael@0 1137 """anyident : IDENTIFIER
michael@0 1138 | CONST"""
michael@0 1139 p[0] = {'value': p[1],
michael@0 1140 'location': self.getLocation(p, 1)}
michael@0 1141
michael@0 1142 def p_attributes(self, p):
michael@0 1143 """attributes : '[' attlist ']'
michael@0 1144 | """
michael@0 1145 if len(p) == 1:
michael@0 1146 p[0] = {'attlist': []}
michael@0 1147 else:
michael@0 1148 p[0] = {'attlist': p[2],
michael@0 1149 'doccomments': p.slice[1].doccomments}
michael@0 1150
michael@0 1151 def p_attlist_start(self, p):
michael@0 1152 """attlist : attribute"""
michael@0 1153 p[0] = [p[1]]
michael@0 1154
michael@0 1155 def p_attlist_continue(self, p):
michael@0 1156 """attlist : attribute ',' attlist"""
michael@0 1157 p[0] = list(p[3])
michael@0 1158 p[0].insert(0, p[1])
michael@0 1159
michael@0 1160 def p_attribute(self, p):
michael@0 1161 """attribute : anyident attributeval"""
michael@0 1162 p[0] = (p[1]['value'], p[2], p[1]['location'])
michael@0 1163
michael@0 1164 def p_attributeval(self, p):
michael@0 1165 """attributeval : '(' IDENTIFIER ')'
michael@0 1166 | '(' IID ')'
michael@0 1167 | """
michael@0 1168 if len(p) > 1:
michael@0 1169 p[0] = p[2]
michael@0 1170
michael@0 1171 def p_interface(self, p):
michael@0 1172 """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
michael@0 1173 atts, INTERFACE, name, base, body, SEMI = p[1:]
michael@0 1174 attlist = atts['attlist']
michael@0 1175 doccomments = []
michael@0 1176 if 'doccomments' in atts:
michael@0 1177 doccomments.extend(atts['doccomments'])
michael@0 1178 doccomments.extend(p.slice[2].doccomments)
michael@0 1179
michael@0 1180 l = lambda: self.getLocation(p, 2)
michael@0 1181
michael@0 1182 if body is None:
michael@0 1183 # forward-declared interface... must not have attributes!
michael@0 1184 if len(attlist) != 0:
michael@0 1185 raise IDLError("Forward-declared interface must not have attributes",
michael@0 1186 list[0][3])
michael@0 1187
michael@0 1188 if base is not None:
michael@0 1189 raise IDLError("Forward-declared interface must not have a base",
michael@0 1190 l())
michael@0 1191 p[0] = Forward(name=name, location=l(), doccomments=doccomments)
michael@0 1192 else:
michael@0 1193 p[0] = Interface(name=name,
michael@0 1194 attlist=attlist,
michael@0 1195 base=base,
michael@0 1196 members=body,
michael@0 1197 location=l(),
michael@0 1198 doccomments=doccomments)
michael@0 1199
michael@0 1200 def p_ifacebody(self, p):
michael@0 1201 """ifacebody : '{' members '}'
michael@0 1202 | """
michael@0 1203 if len(p) > 1:
michael@0 1204 p[0] = p[2]
michael@0 1205
michael@0 1206 def p_ifacebase(self, p):
michael@0 1207 """ifacebase : ':' IDENTIFIER
michael@0 1208 | """
michael@0 1209 if len(p) == 3:
michael@0 1210 p[0] = p[2]
michael@0 1211
michael@0 1212 def p_members_start(self, p):
michael@0 1213 """members : """
michael@0 1214 p[0] = []
michael@0 1215
michael@0 1216 def p_members_continue(self, p):
michael@0 1217 """members : member members"""
michael@0 1218 p[0] = list(p[2])
michael@0 1219 p[0].insert(0, p[1])
michael@0 1220
michael@0 1221 def p_member_cdata(self, p):
michael@0 1222 """member : CDATA"""
michael@0 1223 p[0] = CDATA(p[1], self.getLocation(p, 1))
michael@0 1224
michael@0 1225 def p_member_const(self, p):
michael@0 1226 """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
michael@0 1227 p[0] = ConstMember(type=p[2], name=p[3],
michael@0 1228 value=p[5], location=self.getLocation(p, 1),
michael@0 1229 doccomments=p.slice[1].doccomments)
michael@0 1230
michael@0 1231 # All "number" products return a function(interface)
michael@0 1232
michael@0 1233 def p_number_decimal(self, p):
michael@0 1234 """number : NUMBER"""
michael@0 1235 n = int(p[1])
michael@0 1236 p[0] = lambda i: n
michael@0 1237
michael@0 1238 def p_number_hex(self, p):
michael@0 1239 """number : HEXNUM"""
michael@0 1240 n = int(p[1], 16)
michael@0 1241 p[0] = lambda i: n
michael@0 1242
michael@0 1243 def p_number_identifier(self, p):
michael@0 1244 """number : IDENTIFIER"""
michael@0 1245 id = p[1]
michael@0 1246 loc = self.getLocation(p, 1)
michael@0 1247 p[0] = lambda i: i.getConst(id, loc)
michael@0 1248
michael@0 1249 def p_number_paren(self, p):
michael@0 1250 """number : '(' number ')'"""
michael@0 1251 p[0] = p[2]
michael@0 1252
michael@0 1253 def p_number_neg(self, p):
michael@0 1254 """number : '-' number %prec UMINUS"""
michael@0 1255 n = p[2]
michael@0 1256 p[0] = lambda i: - n(i)
michael@0 1257
michael@0 1258 def p_number_add(self, p):
michael@0 1259 """number : number '+' number
michael@0 1260 | number '-' number
michael@0 1261 | number '*' number"""
michael@0 1262 n1 = p[1]
michael@0 1263 n2 = p[3]
michael@0 1264 if p[2] == '+':
michael@0 1265 p[0] = lambda i: n1(i) + n2(i)
michael@0 1266 elif p[2] == '-':
michael@0 1267 p[0] = lambda i: n1(i) - n2(i)
michael@0 1268 else:
michael@0 1269 p[0] = lambda i: n1(i) * n2(i)
michael@0 1270
michael@0 1271 def p_number_shift(self, p):
michael@0 1272 """number : number LSHIFT number
michael@0 1273 | number RSHIFT number"""
michael@0 1274 n1 = p[1]
michael@0 1275 n2 = p[3]
michael@0 1276 if p[2] == '<<':
michael@0 1277 p[0] = lambda i: n1(i) << n2(i)
michael@0 1278 else:
michael@0 1279 p[0] = lambda i: n1(i) >> n2(i)
michael@0 1280
michael@0 1281 def p_number_bitor(self, p):
michael@0 1282 """number : number '|' number"""
michael@0 1283 n1 = p[1]
michael@0 1284 n2 = p[3]
michael@0 1285 p[0] = lambda i: n1(i) | n2(i)
michael@0 1286
michael@0 1287 def p_member_att(self, p):
michael@0 1288 """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
michael@0 1289 if 'doccomments' in p[1]:
michael@0 1290 doccomments = p[1]['doccomments']
michael@0 1291 elif p[2] is not None:
michael@0 1292 doccomments = p[2]
michael@0 1293 else:
michael@0 1294 doccomments = p.slice[3].doccomments
michael@0 1295
michael@0 1296 p[0] = Attribute(type=p[4],
michael@0 1297 name=p[5],
michael@0 1298 attlist=p[1]['attlist'],
michael@0 1299 readonly=p[2] is not None,
michael@0 1300 location=self.getLocation(p, 3),
michael@0 1301 doccomments=doccomments)
michael@0 1302
michael@0 1303 def p_member_method(self, p):
michael@0 1304 """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
michael@0 1305 if 'doccomments' in p[1]:
michael@0 1306 doccomments = p[1]['doccomments']
michael@0 1307 else:
michael@0 1308 doccomments = p.slice[2].doccomments
michael@0 1309
michael@0 1310 p[0] = Method(type=p[2],
michael@0 1311 name=p[3],
michael@0 1312 attlist=p[1]['attlist'],
michael@0 1313 paramlist=p[5],
michael@0 1314 location=self.getLocation(p, 3),
michael@0 1315 doccomments=doccomments,
michael@0 1316 raises=p[7])
michael@0 1317
michael@0 1318 def p_paramlist(self, p):
michael@0 1319 """paramlist : param moreparams
michael@0 1320 | """
michael@0 1321 if len(p) == 1:
michael@0 1322 p[0] = []
michael@0 1323 else:
michael@0 1324 p[0] = list(p[2])
michael@0 1325 p[0].insert(0, p[1])
michael@0 1326
michael@0 1327 def p_moreparams_start(self, p):
michael@0 1328 """moreparams :"""
michael@0 1329 p[0] = []
michael@0 1330
michael@0 1331 def p_moreparams_continue(self, p):
michael@0 1332 """moreparams : ',' param moreparams"""
michael@0 1333 p[0] = list(p[3])
michael@0 1334 p[0].insert(0, p[2])
michael@0 1335
michael@0 1336 def p_param(self, p):
michael@0 1337 """param : attributes paramtype IDENTIFIER IDENTIFIER"""
michael@0 1338 p[0] = Param(paramtype=p[2],
michael@0 1339 type=p[3],
michael@0 1340 name=p[4],
michael@0 1341 attlist=p[1]['attlist'],
michael@0 1342 location=self.getLocation(p, 3))
michael@0 1343
michael@0 1344 def p_paramtype(self, p):
michael@0 1345 """paramtype : IN
michael@0 1346 | INOUT
michael@0 1347 | OUT"""
michael@0 1348 p[0] = p[1]
michael@0 1349
michael@0 1350 def p_optreadonly(self, p):
michael@0 1351 """optreadonly : READONLY
michael@0 1352 | """
michael@0 1353 if len(p) > 1:
michael@0 1354 p[0] = p.slice[1].doccomments
michael@0 1355 else:
michael@0 1356 p[0] = None
michael@0 1357
michael@0 1358 def p_raises(self, p):
michael@0 1359 """raises : RAISES '(' idlist ')'
michael@0 1360 | """
michael@0 1361 if len(p) == 1:
michael@0 1362 p[0] = []
michael@0 1363 else:
michael@0 1364 p[0] = p[3]
michael@0 1365
michael@0 1366 def p_idlist(self, p):
michael@0 1367 """idlist : IDENTIFIER"""
michael@0 1368 p[0] = [p[1]]
michael@0 1369
michael@0 1370 def p_idlist_continue(self, p):
michael@0 1371 """idlist : IDENTIFIER ',' idlist"""
michael@0 1372 p[0] = list(p[3])
michael@0 1373 p[0].insert(0, p[1])
michael@0 1374
michael@0 1375 def p_error(self, t):
michael@0 1376 if not t:
michael@0 1377 raise IDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None)
michael@0 1378 else:
michael@0 1379 location = Location(self.lexer, t.lineno, t.lexpos)
michael@0 1380 raise IDLError("invalid syntax", location)
michael@0 1381
michael@0 1382 def __init__(self, outputdir=''):
michael@0 1383 self._doccomments = []
michael@0 1384 self.lexer = lex.lex(object=self,
michael@0 1385 outputdir=outputdir,
michael@0 1386 lextab='xpidllex',
michael@0 1387 optimize=1)
michael@0 1388 self.parser = yacc.yacc(module=self,
michael@0 1389 outputdir=outputdir,
michael@0 1390 debug=0,
michael@0 1391 tabmodule='xpidlyacc',
michael@0 1392 optimize=1)
michael@0 1393
michael@0 1394 def clearComments(self):
michael@0 1395 self._doccomments = []
michael@0 1396
michael@0 1397 def token(self):
michael@0 1398 t = self.lexer.token()
michael@0 1399 if t is not None and t.type != 'CDATA':
michael@0 1400 t.doccomments = self._doccomments
michael@0 1401 self._doccomments = []
michael@0 1402 return t
michael@0 1403
michael@0 1404 def parse(self, data, filename=None):
michael@0 1405 if filename is not None:
michael@0 1406 self.lexer.filename = filename
michael@0 1407 self.lexer.lineno = 1
michael@0 1408 self.lexer.input(data)
michael@0 1409 idl = self.parser.parse(lexer=self)
michael@0 1410 if filename is not None:
michael@0 1411 idl.deps.append(filename)
michael@0 1412 return idl
michael@0 1413
michael@0 1414 def getLocation(self, p, i):
michael@0 1415 return Location(self.lexer, p.lineno(i), p.lexpos(i))
michael@0 1416
michael@0 1417 if __name__ == '__main__':
michael@0 1418 p = IDLParser()
michael@0 1419 for f in sys.argv[1:]:
michael@0 1420 print "Parsing %s" % f
michael@0 1421 p.parse(open(f).read(), filename=f)

mercurial