Thu, 22 Jan 2015 13:21:57 +0100
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) |