Back to index

enigmail  1.4.3
xpidl.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 # xpidl.py - A parser for cross-platform IDL (XPIDL) files.
00003 #
00004 # ***** BEGIN LICENSE BLOCK *****
00005 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
00006 #
00007 # The contents of this file are subject to the Mozilla Public License Version
00008 # 1.1 (the "License"); you may not use this file except in compliance with
00009 # the License. You may obtain a copy of the License at
00010 # http://www.mozilla.org/MPL/
00011 #
00012 # Software distributed under the License is distributed on an "AS IS" basis,
00013 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00014 # for the specific language governing rights and limitations under the
00015 # License.
00016 #
00017 # The Original Code is mozilla.org code.
00018 #
00019 # The Initial Developer of the Original Code is
00020 #   Mozilla Foundation.
00021 # Portions created by the Initial Developer are Copyright (C) 2008
00022 # the Initial Developer. All Rights Reserved.
00023 #
00024 # Contributor(s):
00025 #   Benjamin Smedberg <benjamin@smedbergs.us>
00026 #
00027 # Alternatively, the contents of this file may be used under the terms of
00028 # either of the GNU General Public License Version 2 or later (the "GPL"),
00029 # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00030 # in which case the provisions of the GPL or the LGPL are applicable instead
00031 # of those above. If you wish to allow use of your version of this file only
00032 # under the terms of either the GPL or the LGPL, and not to allow others to
00033 # use your version of this file under the terms of the MPL, indicate your
00034 # decision by deleting the provisions above and replace them with the notice
00035 # and other provisions required by the GPL or the LGPL. If you do not delete
00036 # the provisions above, a recipient may use your version of this file under
00037 # the terms of any one of the MPL, the GPL or the LGPL.
00038 #
00039 # ***** END LICENSE BLOCK *****
00040 
00041 """A parser for cross-platform IDL (XPIDL) files."""
00042 
00043 import sys, os.path, re
00044 from ply import lex, yacc
00045 
00046 """A type conforms to the following pattern:
00047 
00048     def isScriptable(self):
00049         'returns True or False'
00050 
00051     def nativeType(self, calltype):
00052         'returns a string representation of the native type
00053         calltype must be 'in', 'out', or 'inout'
00054 
00055 Interface members const/method/attribute conform to the following pattern:
00056 
00057     name = 'string'
00058 
00059     def toIDL(self):
00060         'returns the member signature as IDL'
00061 """
00062 
00063 def attlistToIDL(attlist):
00064     if len(attlist) == 0:
00065         return ''
00066 
00067     attlist = list(attlist)
00068     attlist.sort(cmp=lambda a,b: cmp(a[0], b[0]))
00069 
00070     return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
00071                               for name, value, aloc in attlist])
00072 
00073 _paramsHardcode = {
00074     2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
00075     3: ('array', 'size_is', 'const'),
00076 }
00077 
00078 def paramAttlistToIDL(attlist):
00079     if len(attlist) == 0:
00080         return ''
00081 
00082     # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
00083     # quirk
00084     attlist = list(attlist)
00085     sorted = []
00086     if len(attlist) in _paramsHardcode:
00087         for p in _paramsHardcode[len(attlist)]:
00088             i = 0
00089             while i < len(attlist):
00090                 if attlist[i][0] == p:
00091                     sorted.append(attlist[i])
00092                     del attlist[i]
00093                     continue
00094 
00095                 i += 1
00096 
00097     sorted.extend(attlist)
00098 
00099     return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
00100                                 for name, value, aloc in sorted])
00101 
00102 def unaliasType(t):
00103     while t.kind == 'typedef':
00104         t = t.realtype
00105     assert t is not None
00106     return t
00107 
00108 def getBuiltinOrNativeTypeName(t):
00109     t = unaliasType(t)
00110     if t.kind == 'builtin':
00111         return t.name
00112     elif t.kind == 'native':
00113         assert t.specialtype is not None
00114         return '[%s]' % t.specialtype
00115     else:
00116         return None
00117 
00118 class BuiltinLocation(object):
00119     def get(self):
00120         return "<builtin type>"
00121 
00122     def __str__(self):
00123         return self.get()
00124 
00125 class Builtin(object):
00126     kind = 'builtin'
00127     location = BuiltinLocation
00128 
00129     def __init__(self, name, nativename, signed=False, maybeConst=False):
00130         self.name = name
00131         self.nativename = nativename
00132         self.signed = signed
00133         self.maybeConst = maybeConst
00134 
00135     def isScriptable(self):
00136         return True
00137 
00138     def nativeType(self, calltype, shared=False, const=False):
00139         if const:
00140             print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True)
00141             const = 'const '
00142         elif calltype == 'in' and self.nativename.endswith('*'):
00143             const = 'const '
00144         elif shared:
00145             if not self.nativename.endswith('*'):
00146                 raise IDLError("[shared] not applicable to non-pointer types.", self.location)
00147             const = 'const '
00148         else:
00149             const = ''
00150         return "%s%s %s" % (const, self.nativename,
00151                             calltype != 'in' and '*' or '')
00152 
00153 builtinNames = [
00154     Builtin('boolean', 'bool'),
00155     Builtin('void', 'void'),
00156     Builtin('octet', 'PRUint8'),
00157     Builtin('short', 'PRInt16', True, True),
00158     Builtin('long', 'PRInt32', True, True),
00159     Builtin('long long', 'PRInt64', True, False),
00160     Builtin('unsigned short', 'PRUint16', False, True),
00161     Builtin('unsigned long', 'PRUint32', False, True),
00162     Builtin('unsigned long long', 'PRUint64', False, False),
00163     Builtin('float', 'float', True, False),
00164     Builtin('double', 'double', True, False),
00165     Builtin('char', 'char', True, False),
00166     Builtin('string', 'char *', False, False),
00167     Builtin('wchar', 'PRUnichar', False, False),
00168     Builtin('wstring', 'PRUnichar *', False, False),
00169 ]
00170 
00171 builtinMap = {}
00172 for b in builtinNames:
00173     builtinMap[b.name] = b
00174 
00175 class Location(object):
00176     _line = None
00177 
00178     def __init__(self, lexer, lineno, lexpos):
00179         self._lineno = lineno
00180         self._lexpos = lexpos
00181         self._lexdata = lexer.lexdata
00182         self._file = getattr(lexer, 'filename', "<unknown>")
00183 
00184     def __eq__(self, other):
00185         return self._lexpos == other._lexpos and \
00186                self._file == other._file
00187 
00188     def resolve(self):
00189         if self._line:
00190             return
00191 
00192         startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
00193         endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
00194         self._line = self._lexdata[startofline:endofline]
00195         self._colno = self._lexpos - startofline
00196 
00197     def pointerline(self):
00198         def i():
00199             for i in xrange(0, self._colno):
00200                 yield " "
00201             yield "^"
00202 
00203         return "".join(i())
00204 
00205     def get(self):
00206         self.resolve()
00207         return "%s line %s:%s" % (self._file, self._lineno, self._colno)
00208 
00209     def __str__(self):
00210         self.resolve()
00211         return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
00212                                           self._line, self.pointerline())
00213 
00214 class NameMap(object):
00215     """Map of name -> object. Each object must have a .name and .location property.
00216     Setting the same name twice throws an error."""
00217     def __init__(self):
00218         self._d = {}
00219 
00220     def __getitem__(self, key):
00221         if key in builtinMap:
00222             return builtinMap[key]
00223         return self._d[key]
00224 
00225     def __iter__(self):
00226         return self._d.itervalues()
00227 
00228     def __contains__(self, key):
00229         return key in builtinMap or key in self._d
00230 
00231     def set(self, object):
00232         if object.name in builtinMap:
00233             raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location)
00234         if object.name in self._d:
00235             old = self._d[object.name]
00236             if old == object: return
00237             if isinstance(old, Forward) and isinstance(object, Interface):
00238                 self._d[object.name] = object
00239             elif isinstance(old, Interface) and isinstance(object, Forward):
00240                 pass
00241             else:
00242                 raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location)
00243         else:
00244             self._d[object.name] = object
00245 
00246     def get(self, id, location):
00247         try:
00248             return self[id]
00249         except KeyError:
00250             raise IDLError("Name '%s' not found", location)
00251 
00252 class IDLError(Exception):
00253     def __init__(self, message, location, warning=False):
00254         self.message = message
00255         self.location = location
00256         self.warning = warning
00257 
00258     def __str__(self):
00259         return "%s: %s, %s" % (self.warning and 'warning' or 'error',
00260                                self.message, self.location)
00261 
00262 class Include(object):
00263     kind = 'include'
00264 
00265     def __init__(self, filename, location):
00266         self.filename = filename
00267         self.location = location
00268 
00269     def __str__(self):
00270         return "".join(["include '%s'\n" % self.filename])
00271 
00272     def resolve(self, parent):
00273         def incfiles():
00274             yield self.filename
00275             for dir in parent.incdirs:
00276                 yield os.path.join(dir, self.filename)
00277 
00278         for file in incfiles():
00279             if not os.path.exists(file): continue
00280 
00281             self.IDL = parent.parser.parse(open(file).read(), filename=file)
00282             self.IDL.resolve(parent.incdirs, parent.parser)
00283             for type in self.IDL.getNames():
00284                 parent.setName(type)
00285             parent.deps.extend(self.IDL.deps)
00286             return
00287 
00288         raise IDLError("File '%s' not found" % self.filename, self.location)
00289 
00290 class IDL(object):
00291     def __init__(self, productions):
00292         self.productions = productions
00293         self.deps = []
00294 
00295     def setName(self, object):
00296         self.namemap.set(object)
00297 
00298     def getName(self, id, location):
00299         try:
00300             return self.namemap[id]
00301         except KeyError:
00302             raise IDLError("type '%s' not found" % id, location)
00303 
00304     def hasName(self, id):
00305         return id in self.namemap
00306 
00307     def getNames(self):
00308         return iter(self.namemap)
00309 
00310     def __str__(self):
00311         return "".join([str(p) for p in self.productions])
00312 
00313     def resolve(self, incdirs, parser):
00314         self.namemap = NameMap()
00315         self.incdirs = incdirs
00316         self.parser = parser
00317         for p in self.productions:
00318             p.resolve(self)
00319 
00320     def includes(self):
00321         for p in self.productions:
00322             if p.kind == 'include':
00323                 yield p
00324 
00325     def needsJSTypes(self):
00326         for p in self.productions:
00327             if p.kind == 'interface' and p.needsJSTypes():
00328                 return True
00329         return False
00330 
00331 class CDATA(object):
00332     kind = 'cdata'
00333     _re = re.compile(r'\n+')
00334 
00335     def __init__(self, data, location):
00336         self.data = self._re.sub('\n', data)
00337         self.location = location
00338 
00339     def resolve(self, parent):
00340         pass
00341 
00342     def __str__(self):
00343         return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
00344 
00345     def count(self):
00346         return 0
00347 
00348 class Typedef(object):
00349     kind = 'typedef'
00350 
00351     def __init__(self, type, name, location, doccomments):
00352         self.type = type
00353         self.name = name
00354         self.location = location
00355         self.doccomments = doccomments
00356 
00357     def __eq__(self, other):
00358         return self.name == other.name and self.type == other.type
00359 
00360     def resolve(self, parent):
00361         parent.setName(self)
00362         self.realtype = parent.getName(self.type, self.location)
00363 
00364     def isScriptable(self):
00365         return self.realtype.isScriptable()
00366 
00367     def nativeType(self, calltype):
00368         return "%s %s" % (self.name,
00369                           calltype != 'in' and '*' or '')
00370 
00371     def __str__(self):
00372         return "typedef %s %s\n" % (self.type, self.name)
00373 
00374 class Forward(object):
00375     kind = 'forward'
00376 
00377     def __init__(self, name, location, doccomments):
00378         self.name = name
00379         self.location = location
00380         self.doccomments = doccomments
00381 
00382     def __eq__(self, other):
00383         return other.kind == 'forward' and other.name == self.name
00384 
00385     def resolve(self, parent):
00386         # Hack alert: if an identifier is already present, move the doccomments
00387         # forward.
00388         if parent.hasName(self.name):
00389             for i in xrange(0, len(parent.productions)):
00390                 if parent.productions[i] is self: break
00391             for i in xrange(i + 1, len(parent.productions)):
00392                 if hasattr(parent.productions[i], 'doccomments'):
00393                     parent.productions[i].doccomments[0:0] = self.doccomments
00394                     break
00395 
00396         parent.setName(self)
00397 
00398     def isScriptable(self):
00399         return True
00400 
00401     def nativeType(self, calltype):
00402         return "%s %s" % (self.name,
00403                           calltype != 'in' and '* *' or '*')
00404 
00405     def __str__(self):
00406         return "forward-declared %s\n" % self.name
00407 
00408 class Native(object):
00409     kind = 'native'
00410 
00411     modifier = None
00412     specialtype = None
00413 
00414     specialtypes = {
00415         'nsid': None,
00416         'domstring': 'nsAString',
00417         'utf8string': 'nsACString',
00418         'cstring': 'nsACString',
00419         'astring': 'nsAString',
00420         'jsval': 'JS::Value'
00421         }
00422 
00423     def __init__(self, name, nativename, attlist, location):
00424         self.name = name
00425         self.nativename = nativename
00426         self.location = location
00427 
00428         for name, value, aloc in attlist:
00429             if value is not None:
00430                 raise IDLError("Unexpected attribute value", aloc)
00431             if name in ('ptr', 'ref'):
00432                 if self.modifier is not None:
00433                     raise IDLError("More than one ptr/ref modifier", aloc)
00434                 self.modifier = name
00435             elif name in self.specialtypes.keys():
00436                 if self.specialtype is not None:
00437                     raise IDLError("More than one special type", aloc)
00438                 self.specialtype = name
00439                 if self.specialtypes[name] is not None:
00440                     self.nativename = self.specialtypes[name]
00441             else:
00442                 raise IDLError("Unexpected attribute", aloc)
00443 
00444     def __eq__(self, other):
00445         return self.name == other.name and \
00446                self.nativename == other.nativename and \
00447                self.modifier == other.modifier and \
00448                self.specialtype == other.specialtype
00449 
00450     def resolve(self, parent):
00451         parent.setName(self)
00452 
00453     def isScriptable(self):
00454         if self.specialtype is None:
00455             return False
00456 
00457         if self.specialtype == 'nsid':
00458             return self.modifier is not None
00459 
00460         return self.modifier == 'ref'
00461 
00462     def isPtr(self, calltype):
00463         return self.modifier == 'ptr' or (self.modifier == 'ref' and self.specialtype == 'jsval' and calltype == 'out')
00464 
00465     def isRef(self, calltype):
00466         return self.modifier == 'ref' and not (self.specialtype == 'jsval' and calltype == 'out')
00467 
00468     def nativeType(self, calltype, const=False, shared=False):
00469         if shared:
00470             if calltype != 'out':
00471                 raise IDLError("[shared] only applies to out parameters.")
00472             const = True
00473 
00474         if self.specialtype is not None and calltype == 'in':
00475             const = True
00476 
00477         if self.isRef(calltype):
00478             m = '& '
00479         elif self.isPtr(calltype):
00480             m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '')
00481         else:
00482             m = calltype != 'in' and '*' or ''
00483         return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
00484 
00485     def __str__(self):
00486         return "native %s(%s)\n" % (self.name, self.nativename)
00487 
00488 class BaseInterface(object):
00489     def __init__(self, name, attlist, base, members, location, doccomments):
00490         self.name = name
00491         self.attributes = InterfaceAttributes(attlist, location)
00492         self.base = base
00493         self.members = members
00494         self.location = location
00495         self.namemap = NameMap()
00496         self.doccomments = doccomments
00497         self.nativename = name
00498 
00499         for m in members:
00500             if not isinstance(m, CDATA):
00501                 self.namemap.set(m)
00502 
00503         self.ops = {
00504             'index':
00505                 {
00506                     'getter': None,
00507                     'setter': None
00508                 },
00509             'name':
00510                 {
00511                     'getter': None,
00512                     'setter': None
00513                 },
00514             'stringifier': None
00515             }
00516 
00517     def __eq__(self, other):
00518         return self.name == other.name and self.location == other.location
00519 
00520     def resolve(self, parent):
00521         self.idl = parent
00522 
00523         # Hack alert: if an identifier is already present, libIDL assigns
00524         # doc comments incorrectly. This is quirks-mode extraordinaire!
00525         if parent.hasName(self.name):
00526             for member in self.members:
00527                 if hasattr(member, 'doccomments'):
00528                     member.doccomments[0:0] = self.doccomments
00529                     break
00530             self.doccomments = parent.getName(self.name, None).doccomments
00531 
00532         if self.attributes.function:
00533             has_method = False
00534             for member in self.members:
00535                 if member.kind is 'method':
00536                     if has_method:
00537                         raise IDLError("interface '%s' has multiple methods, but marked 'function'" % self.name, self.location)
00538                     else:
00539                         has_method = True
00540 
00541         parent.setName(self)
00542         if self.base is not None:
00543             realbase = parent.getName(self.base, self.location)
00544             if realbase.kind != self.kind:
00545                 raise IDLError("%s '%s' inherits from non-%s type '%s'" % (self.kind, self.name, self.kind, self.base), self.location)
00546 
00547             if self.attributes.scriptable and not realbase.attributes.scriptable:
00548                 print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
00549 
00550         forwardedMembers = set()
00551         for member in self.members:
00552             member.resolve(self)
00553             if member.kind is 'method' and member.forward:
00554                 forwardedMembers.add(member.forward)
00555         for member in self.members:
00556             if member.kind is 'method' and member.name in forwardedMembers:
00557                 forwardedMembers.remove(member.name)
00558         for member in forwardedMembers:
00559             raise IDLError("member '%s' on interface '%s' forwards to '%s' which is not on the interface itself" % (member.name, self.name, member.forward), self.location)
00560 
00561         # The number 250 is NOT arbitrary; this number is the maximum number of
00562         # stub entries defined in xpcom/reflect/xptcall/public/genstubs.pl
00563         # Do not increase this value without increasing the number in that
00564         # location, or you WILL cause otherwise unknown problems!
00565         if self.countEntries() > 250 and not self.attributes.builtinclass:
00566             raise IDLError("interface '%s' has too many entries" % self.name,
00567                 self.location)
00568 
00569     def isScriptable(self):
00570         # NOTE: this is not whether *this* interface is scriptable... it's
00571         # whether, when used as a type, it's scriptable, which is true of all
00572         # interfaces.
00573         return True
00574 
00575     def nativeType(self, calltype, const=False):
00576         return "%s%s %s" % (const and 'const ' or '',
00577                             self.name,
00578                             calltype != 'in' and '* *' or '*')
00579 
00580     def __str__(self):
00581         l = ["interface %s\n" % self.name]
00582         if self.base is not None:
00583             l.append("\tbase %s\n" % self.base)
00584         l.append(str(self.attributes))
00585         if self.members is None:
00586             l.append("\tincomplete type\n")
00587         else:
00588             for m in self.members:
00589                 l.append(str(m))
00590         return "".join(l)
00591 
00592     def getConst(self, name, location):
00593         # The constant may be in a base class
00594         iface = self
00595         while name not in iface.namemap and iface is not None:
00596             iface = self.idl.getName(self.base, self.location)
00597         if iface is None:
00598             raise IDLError("cannot find symbol '%s'" % name, c.location)
00599         c = iface.namemap.get(name, location)
00600         if c.kind != 'const':
00601             raise IDLError("symbol '%s' is not a constant", c.location)
00602 
00603         return c.getValue()
00604 
00605     def needsJSTypes(self):
00606         for m in self.members:
00607             if m.kind == "attribute" and m.type == "jsval":
00608                 return True
00609             if m.kind == "method" and m.needsJSTypes():
00610                 return True
00611         return False
00612 
00613     def countEntries(self):
00614         ''' Returns the number of entries in the vtable for this interface. '''
00615         total = sum(member.count() for member in self.members)
00616         if self.base is not None:
00617             realbase = self.idl.getName(self.base, self.location)
00618             total += realbase.countEntries()
00619         return total
00620 
00621 class Interface(BaseInterface):
00622     kind = 'interface'
00623 
00624     def __init__(self, name, attlist, base, members, location, doccomments):
00625         BaseInterface.__init__(self, name, attlist, base, members, location, doccomments)
00626 
00627         if self.attributes.uuid is None:
00628             raise IDLError("interface has no uuid", location)
00629 
00630 class Dictionary(BaseInterface):
00631     kind = 'dictionary'
00632 
00633     def __init__(self, name, attlist, base, members, location, doccomments):
00634         BaseInterface.__init__(self, name, attlist, base, members, location, doccomments)
00635 
00636 class InterfaceAttributes(object):
00637     uuid = None
00638     scriptable = False
00639     builtinclass = False
00640     function = False
00641     deprecated = False
00642     noscript = False
00643 
00644     def setuuid(self, value):
00645         self.uuid = value.lower()
00646 
00647     def setscriptable(self):
00648         self.scriptable = True
00649 
00650     def setfunction(self):
00651         self.function = True
00652 
00653     def setnoscript(self):
00654         self.noscript = True
00655 
00656     def setbuiltinclass(self):
00657         self.builtinclass = True
00658 
00659     def setdeprecated(self):
00660         self.deprecated = True
00661 
00662     actions = {
00663         'uuid':       (True, setuuid),
00664         'scriptable': (False, setscriptable),
00665         'builtinclass': (False, setbuiltinclass),
00666         'function':   (False, setfunction),
00667         'noscript':   (False, setnoscript),
00668         'deprecated': (False, setdeprecated),
00669         'object':     (False, lambda self: True),
00670         }
00671 
00672     def __init__(self, attlist, location):
00673         def badattribute(self):
00674             raise IDLError("Unexpected interface attribute '%s'" % name, location)
00675 
00676         for name, val, aloc in attlist:
00677             hasval, action = self.actions.get(name, (False, badattribute))
00678             if hasval:
00679                 if val is None:
00680                     raise IDLError("Expected value for attribute '%s'" % name,
00681                                    aloc)
00682 
00683                 action(self, val)
00684             else:
00685                 if val is not None:
00686                     raise IDLError("Unexpected value for attribute '%s'" % name,
00687                                    aloc)
00688 
00689                 action(self)
00690 
00691     def __str__(self):
00692         l = []
00693         if self.uuid:
00694             l.append("\tuuid: %s\n" % self.uuid)
00695         if self.scriptable:
00696             l.append("\tscriptable\n")
00697         if self.builtinclass:
00698             l.append("\tbuiltinclass\n")
00699         if self.function:
00700             l.append("\tfunction\n")
00701         return "".join(l)
00702 
00703 class ConstMember(object):
00704     kind = 'const'
00705     def __init__(self, type, name, value, location, doccomments):
00706         self.type = type
00707         self.name = name
00708         self.value = value
00709         self.location = location
00710         self.doccomments = doccomments
00711 
00712     def resolve(self, parent):
00713         self.realtype = parent.idl.getName(self.type, self.location)
00714         self.iface = parent
00715         basetype = self.realtype
00716         while isinstance(basetype, Typedef):
00717             basetype = basetype.realtype
00718         if not isinstance(basetype, Builtin) or not basetype.maybeConst:
00719             raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
00720 
00721         self.basetype = basetype
00722 
00723     def getValue(self):
00724         return self.value(self.iface)
00725 
00726     def __str__(self):
00727         return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
00728 
00729     def count(self):
00730         return 0
00731 
00732 class Attribute(object):
00733     kind = 'attribute'
00734     noscript = False
00735     notxpcom = False
00736     readonly = False
00737     implicit_jscontext = False
00738     nostdcall = False
00739     binaryname = None
00740     null = None
00741     undefined = None
00742     deprecated = False
00743     nullable = False
00744     defvalue = None
00745 
00746     def __init__(self, type, name, attlist, readonly, nullable, defvalue, location, doccomments):
00747         self.type = type
00748         self.name = name
00749         self.attlist = attlist
00750         self.readonly = readonly
00751         self.nullable = nullable
00752         self.defvalue = defvalue
00753         self.location = location
00754         self.doccomments = doccomments
00755 
00756         for name, value, aloc in attlist:
00757             if name == 'binaryname':
00758                 if value is None:
00759                     raise IDLError("binaryname attribute requires a value",
00760                                    aloc)
00761 
00762                 self.binaryname = value
00763                 continue
00764 
00765             if name == 'Null':
00766                 if value is None:
00767                     raise IDLError("'Null' attribute requires a value", aloc)
00768                 if readonly:
00769                     raise IDLError("'Null' attribute only makes sense for setters",
00770                                    aloc);
00771                 if value not in ('Empty', 'Null', 'Stringify'):
00772                     raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'",
00773                                    aloc);
00774                 self.null = value
00775             elif name == 'Undefined':
00776                 if value is None:
00777                     raise IDLError("'Undefined' attribute requires a value", aloc)
00778                 if readonly:
00779                     raise IDLError("'Undefined' attribute only makes sense for setters",
00780                                    aloc);
00781                 if value not in ('Empty', 'Null'):
00782                     raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'",
00783                                    aloc);
00784                 self.undefined = value
00785             else:
00786                 if value is not None:
00787                     raise IDLError("Unexpected attribute value", aloc)
00788 
00789                 if name == 'noscript':
00790                     self.noscript = True
00791                 elif name == 'notxpcom':
00792                     self.notxpcom = True
00793                 elif name == 'implicit_jscontext':
00794                     self.implicit_jscontext = True
00795                 elif name == 'deprecated':
00796                     self.deprecated = True
00797                 elif name == 'nostdcall':
00798                     self.nostdcall = True
00799                 else:
00800                     raise IDLError("Unexpected attribute '%s'" % name, aloc)
00801 
00802     def resolve(self, iface):
00803         self.iface = iface
00804         self.realtype = iface.idl.getName(self.type, self.location)
00805         if (self.null is not None and
00806             getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
00807             raise IDLError("'Null' attribute can only be used on DOMString",
00808                            self.location)
00809         if (self.undefined is not None and
00810             getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
00811             raise IDLError("'Undefined' attribute can only be used on DOMString",
00812                            self.location)
00813         if (self.nullable and
00814             getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
00815             raise IDLError("Nullable types (T?) is supported only for DOMString",
00816                            self.location)
00817 
00818     def toIDL(self):
00819         attribs = attlistToIDL(self.attlist)
00820         readonly = self.readonly and 'readonly ' or ''
00821         return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
00822         
00823     def isScriptable(self):
00824         if not self.iface.attributes.scriptable: return False
00825         return not (self.noscript or self.notxpcom)
00826 
00827     def __str__(self):
00828         return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
00829                                           self.type, self.name)
00830 
00831     def count(self):
00832         return self.readonly and 1 or 2
00833 
00834 class Method(object):
00835     kind = 'method'
00836     noscript = False
00837     notxpcom = False
00838     binaryname = None
00839     implicit_jscontext = False
00840     nostdcall = False
00841     optional_argc = False
00842     deprecated = False
00843     getter = False
00844     setter = False
00845     stringifier = False
00846     forward = None
00847 
00848     def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
00849         self.type = type
00850         self.name = name
00851         self.attlist = attlist
00852         self.params = paramlist
00853         self.location = location
00854         self.doccomments = doccomments
00855         self.raises = raises
00856 
00857         for name, value, aloc in attlist:
00858             if name == 'binaryname':
00859                 if value is None:
00860                     raise IDLError("binaryname attribute requires a value",
00861                                    aloc)
00862 
00863                 self.binaryname = value
00864                 continue
00865             if name == 'forward':
00866                 if value is None:
00867                     raise IDLError("forward attribute requires a value",
00868                                    aloc)
00869 
00870                 self.forward = value
00871                 continue
00872 
00873             if value is not None:
00874                 raise IDLError("Unexpected attribute value", aloc)
00875 
00876             if name == 'noscript':
00877                 self.noscript = True
00878             elif name == 'notxpcom':
00879                 self.notxpcom = True
00880             elif name == 'implicit_jscontext':
00881                 self.implicit_jscontext = True
00882             elif name == 'optional_argc':
00883                 self.optional_argc = True
00884             elif name == 'deprecated':
00885                 self.deprecated = True
00886             elif name == 'nostdcall':
00887                 self.nostdcall = True
00888             elif name == 'getter':
00889                 if (len(self.params) != 1):
00890                     raise IDLError("Methods marked as getter must take 1 argument", aloc)
00891                 self.getter = True
00892             elif name == 'setter':
00893                 if (len(self.params) != 2):
00894                     raise IDLError("Methods marked as setter must take 2 arguments", aloc)
00895                 self.setter = True
00896             elif name == 'stringifier':
00897                 if (len(self.params) != 0):
00898                     raise IDLError("Methods marked as stringifier must take 0 arguments", aloc)
00899                 self.stringifier = True
00900             else:
00901                 raise IDLError("Unexpected attribute '%s'" % name, aloc)
00902 
00903         self.namemap = NameMap()
00904         for p in paramlist:
00905             self.namemap.set(p)
00906 
00907     def resolve(self, iface):
00908         self.iface = iface
00909         self.realtype = self.iface.idl.getName(self.type, self.location)
00910         for p in self.params:
00911             p.resolve(self)
00912         if self.getter:
00913             if getBuiltinOrNativeTypeName(self.params[0].realtype) == 'unsigned long':
00914                 ops = 'index'
00915             else:
00916                 if getBuiltinOrNativeTypeName(self.params[0].realtype) != '[domstring]':
00917                     raise IDLError("a getter must take a single unsigned long or DOMString argument" % self.iface.name, self.location)
00918                 ops = 'name'
00919             if self.iface.ops[ops]['getter']:
00920                 raise IDLError("a %s getter was already defined on interface '%s'" % (ops, self.iface.name), self.location)
00921             self.iface.ops[ops]['getter'] = self
00922         if self.setter:
00923             if getBuiltinOrNativeTypeName(self.params[0].realtype) == 'unsigned long':
00924                 ops = 'index'
00925             else:
00926                 if getBuiltinOrNativeTypeName(self.params[0].realtype) != '[domstring]':
00927                     print getBuiltinOrNativeTypeName(self.params[0].realtype)
00928                     raise IDLError("a setter must take a unsigned long or DOMString argument" % self.iface.name, self.location)
00929                 ops = 'name'
00930             if self.iface.ops[ops]['setter']:
00931                 raise IDLError("a %s setter was already defined on interface '%s'" % (ops, self.iface.name), self.location)
00932             self.iface.ops[ops]['setter'] = self
00933         if self.stringifier:
00934             if self.iface.ops['stringifier']:
00935                 raise IDLError("a stringifier was already defined on interface '%s'" % self.iface.name, self.location)
00936             if getBuiltinOrNativeTypeName(self.realtype) != '[domstring]':
00937                 raise IDLError("'stringifier' attribute can only be used on methods returning DOMString",
00938                                self.location)
00939             self.iface.ops['stringifier'] = self
00940         for p in self.params:
00941             if p.size_is:
00942                 found_size_param = False
00943                 for size_param in self.params:
00944                     if p.size_is == size_param.name:
00945                         found_size_param = True
00946                         if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long':
00947                             raise IDLError("is_size parameter must have type 'unsigned long'", self.location)
00948                 if not found_size_param:
00949                     raise IDLError("could not find is_size parameter '%s'" % p.size_is, self.location)
00950 
00951     def isScriptable(self):
00952         if not self.iface.attributes.scriptable: return False
00953         return not (self.noscript or self.notxpcom)
00954 
00955     def __str__(self):
00956         return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
00957 
00958     def toIDL(self):
00959         if len(self.raises):
00960             raises = ' raises (%s)' % ','.join(self.raises)
00961         else:
00962             raises = ''
00963 
00964         return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
00965                                     self.type,
00966                                     self.name,
00967                                     ", ".join([p.toIDL()
00968                                                for p in self.params]),
00969                                     raises)
00970 
00971     def needsJSTypes(self):
00972         if self.implicit_jscontext:
00973             return True
00974         if self.type == "jsval":
00975             return True
00976         for p in self.params:
00977             t = p.realtype
00978             if isinstance(t, Native) and t.specialtype == "jsval":
00979                 return True
00980         return False
00981 
00982     def count(self):
00983         return 1
00984 
00985 class Param(object):
00986     size_is = None
00987     iid_is = None
00988     const = False
00989     array = False
00990     retval = False
00991     shared = False
00992     optional = False
00993     null = None
00994     undefined = None
00995 
00996     def __init__(self, paramtype, type, name, attlist, location, realtype=None):
00997         self.paramtype = paramtype
00998         self.type = type
00999         self.name = name
01000         self.attlist = attlist
01001         self.location = location
01002         self.realtype = realtype
01003 
01004         for name, value, aloc in attlist:
01005             # Put the value-taking attributes first!
01006             if name == 'size_is':
01007                 if value is None:
01008                     raise IDLError("'size_is' must specify a parameter", aloc)
01009                 self.size_is = value
01010             elif name == 'iid_is':
01011                 if value is None:
01012                     raise IDLError("'iid_is' must specify a parameter", aloc)
01013                 self.iid_is = value
01014             elif name == 'Null':
01015                 if value is None:
01016                     raise IDLError("'Null' must specify a parameter", aloc)
01017                 if value not in ('Empty', 'Null', 'Stringify'):
01018                     raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'",
01019                                    aloc);
01020                 self.null = value
01021             elif name == 'Undefined':
01022                 if value is None:
01023                     raise IDLError("'Undefined' must specify a parameter", aloc)
01024                 if value not in ('Empty', 'Null'):
01025                     raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'",
01026                                    aloc);
01027                 self.undefined = value
01028             else:
01029                 if value is not None:
01030                     raise IDLError("Unexpected value for attribute '%s'" % name,
01031                                    aloc)
01032 
01033                 if name == 'const':
01034                     self.const = True
01035                 elif name == 'array':
01036                     self.array = True
01037                 elif name == 'retval':
01038                     self.retval = True
01039                 elif name == 'shared':
01040                     self.shared = True
01041                 elif name == 'optional':
01042                     self.optional = True
01043                 else:
01044                     raise IDLError("Unexpected attribute '%s'" % name, aloc)
01045 
01046     def resolve(self, method):
01047         self.realtype = method.iface.idl.getName(self.type, self.location)
01048         if self.array:
01049             self.realtype = Array(self.realtype)
01050         if (self.null is not None and
01051             getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
01052             raise IDLError("'Null' attribute can only be used on DOMString",
01053                            self.location)
01054         if (self.undefined is not None and
01055             getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'):
01056             raise IDLError("'Undefined' attribute can only be used on DOMString",
01057                            self.location)
01058 
01059     def nativeType(self):
01060         kwargs = {}
01061         if self.shared: kwargs['shared'] = True
01062         if self.const: kwargs['const'] = True
01063 
01064         try:
01065             return self.realtype.nativeType(self.paramtype, **kwargs)
01066         except IDLError, e:
01067             raise IDLError(e.message, self.location)
01068         except TypeError, e:
01069             raise IDLError("Unexpected parameter attribute", self.location)
01070 
01071     def toIDL(self):
01072         return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
01073                                self.paramtype,
01074                                self.type,
01075                                self.name)
01076 
01077 class Array(object):
01078     def __init__(self, basetype):
01079         self.type = basetype
01080 
01081     def isScriptable(self):
01082         return self.type.isScriptable()
01083 
01084     def nativeType(self, calltype, const=False):
01085         return "%s%s*" % (const and 'const ' or '',
01086                           self.type.nativeType(calltype))
01087 
01088 class IDLParser(object):
01089     keywords = {
01090         'const': 'CONST',
01091         'interface': 'INTERFACE',
01092         'dictionary': 'DICTIONARY',
01093         'in': 'IN',
01094         'inout': 'INOUT',
01095         'out': 'OUT',
01096         'attribute': 'ATTRIBUTE',
01097         'raises': 'RAISES',
01098         'readonly': 'READONLY',
01099         'native': 'NATIVE',
01100         'typedef': 'TYPEDEF'
01101         }
01102 
01103     tokens = [
01104         'IDENTIFIER',
01105         'CDATA',
01106         'INCLUDE',
01107         'IID',
01108         'NUMBER',
01109         'HEXNUM',
01110         'LSHIFT',
01111         'RSHIFT',
01112         'NATIVEID',
01113         'STRING',
01114         ]
01115 
01116     tokens.extend(keywords.values())
01117 
01118     states = (
01119         ('nativeid', 'exclusive'),
01120     )
01121 
01122     hexchar = r'[a-fA-F0-9]'
01123 
01124     t_NUMBER = r'-?\d+'
01125     t_HEXNUM = r'0x%s+' % hexchar
01126     t_LSHIFT = r'<<'
01127     t_RSHIFT=  r'>>'
01128 
01129     literals = '"(){}[],;:=|+-*?'
01130 
01131     t_ignore = ' \t'
01132 
01133     def t_multilinecomment(self, t):
01134         r'/\*(?s).*?\*/'
01135         t.lexer.lineno += t.value.count('\n')
01136         if t.value.startswith("/**"):
01137             self._doccomments.append(t.value)
01138 
01139     def t_singlelinecomment(self, t):
01140         r'(?m)//.*?$'
01141 
01142     def t_IID(self, t):
01143         return t
01144     t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
01145 
01146     def t_IDENTIFIER(self, t):
01147         r'unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long|[A-Za-z][A-Za-z_0-9]*'
01148         t.type = self.keywords.get(t.value, 'IDENTIFIER')
01149         return t
01150 
01151     def t_LCDATA(self, t):
01152         r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
01153         t.type = 'CDATA'
01154         t.value = t.lexer.lexmatch.group('cdata')
01155         t.lexer.lineno += t.value.count('\n')
01156         return t
01157 
01158     def t_INCLUDE(self, t):
01159         r'\#include[ \t]+"[^"\n]+"'
01160         inc, value, end = t.value.split('"')
01161         t.value = value
01162         return t
01163 
01164     def t_STRING(self, t):
01165         r'"[^"\n]+"'
01166         begin, value, end = t.value.split('"')
01167         t.value = value
01168         return t
01169 
01170     def t_directive(self, t):
01171         r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
01172         raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
01173                        Location(lexer=self.lexer, lineno=self.lexer.lineno,
01174                                 lexpos=self.lexer.lexpos))
01175 
01176     def t_newline(self, t):
01177         r'\n+'
01178         t.lexer.lineno += len(t.value)
01179 
01180     def t_nativeid_NATIVEID(self, t):
01181         r'[^()\n]+(?=\))'
01182         t.lexer.begin('INITIAL')
01183         return t
01184 
01185     t_nativeid_ignore = ''
01186 
01187     def t_ANY_error(self, t):
01188         raise IDLError("unrecognized input",
01189                        Location(lexer=self.lexer,
01190                                 lineno=self.lexer.lineno,
01191                                 lexpos=self.lexer.lexpos))
01192 
01193     precedence = (
01194         ('left', '|'),
01195         ('left', 'LSHIFT', 'RSHIFT'),
01196         ('left', '+', '-'),
01197         ('left', '*'),
01198         ('left', 'UMINUS'),
01199     )
01200 
01201     def p_idlfile(self, p):
01202         """idlfile : productions"""
01203         p[0] = IDL(p[1])
01204 
01205     def p_productions_start(self, p):
01206         """productions : """
01207         p[0] = []
01208 
01209     def p_productions_cdata(self, p):
01210         """productions : CDATA productions"""
01211         p[0] = list(p[2])
01212         p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
01213 
01214     def p_productions_include(self, p):
01215         """productions : INCLUDE productions"""
01216         p[0] = list(p[2])
01217         p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
01218 
01219     def p_productions_interface(self, p):
01220         """productions : interface productions
01221                        | dictionary productions
01222                        | typedef productions
01223                        | native productions"""
01224         p[0] = list(p[2])
01225         p[0].insert(0, p[1])
01226 
01227     def p_typedef(self, p):
01228         """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
01229         p[0] = Typedef(type=p[2],
01230                        name=p[3],
01231                        location=self.getLocation(p, 1),
01232                        doccomments=p.slice[1].doccomments)
01233 
01234     def p_native(self, p):
01235         """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
01236         p[0] = Native(name=p[3],
01237                       nativename=p[6],
01238                       attlist=p[1]['attlist'],
01239                       location=self.getLocation(p, 2))
01240 
01241     def p_afternativeid(self, p):
01242         """afternativeid : """
01243         # this is a place marker: we switch the lexer into literal identifier
01244         # mode here, to slurp up everything until the closeparen
01245         self.lexer.begin('nativeid')
01246 
01247     def p_anyident(self, p):
01248         """anyident : IDENTIFIER
01249                     | CONST"""
01250         p[0] = {'value': p[1],
01251                 'location': self.getLocation(p, 1)}
01252 
01253     def p_attributes(self, p):
01254         """attributes : '[' attlist ']'
01255                       | """
01256         if len(p) == 1:
01257             p[0] = {'attlist': []}
01258         else:
01259             p[0] = {'attlist': p[2],
01260                     'doccomments': p.slice[1].doccomments}
01261 
01262     def p_attlist_start(self, p):
01263         """attlist : attribute"""
01264         p[0] = [p[1]]
01265 
01266     def p_attlist_continue(self, p):
01267         """attlist : attribute ',' attlist"""
01268         p[0] = list(p[3])
01269         p[0].insert(0, p[1])
01270 
01271     def p_attribute(self, p):
01272         """attribute : anyident attributeval"""
01273         p[0] = (p[1]['value'], p[2], p[1]['location'])
01274 
01275     def p_attributeval(self, p):
01276         """attributeval : '(' IDENTIFIER ')'
01277                         | '(' IID ')'
01278                         | """
01279         if len(p) > 1:
01280             p[0] = p[2]
01281 
01282     def p_interface(self, p):
01283         """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
01284         atts, INTERFACE, name, base, body, SEMI = p[1:]
01285         attlist = atts['attlist']
01286         doccomments = []
01287         if 'doccomments' in atts:
01288             doccomments.extend(atts['doccomments'])
01289         doccomments.extend(p.slice[2].doccomments)
01290 
01291         l = lambda: self.getLocation(p, 2)
01292 
01293         if body is None:
01294             # forward-declared interface... must not have attributes!
01295             if len(attlist) != 0:
01296                 raise IDLError("Forward-declared interface must not have attributes",
01297                                list[0][3])
01298 
01299             if base is not None:
01300                 raise IDLError("Forward-declared interface must not have a base",
01301                                l())
01302             p[0] = Forward(name=name, location=l(), doccomments=doccomments)
01303         else:
01304             p[0] = Interface(name=name,
01305                              attlist=attlist,
01306                              base=base,
01307                              members=body,
01308                              location=l(),
01309                              doccomments=doccomments)
01310 
01311     def p_ifacebody(self, p):
01312         """ifacebody : '{' members '}'
01313                      | """
01314         if len(p) > 1:
01315             p[0] = p[2]
01316 
01317     def p_ifacebase(self, p):
01318         """ifacebase : ':' IDENTIFIER
01319                      | """
01320         if len(p) == 3:
01321             p[0] = p[2]
01322 
01323     def p_members_start(self, p):
01324         """members : """
01325         p[0] = []
01326 
01327     def p_members_continue(self, p):
01328         """members : member members"""
01329         p[0] = list(p[2])
01330         p[0].insert(0, p[1])
01331 
01332     def p_member_cdata(self, p):
01333         """member : CDATA"""
01334         p[0] = CDATA(p[1], self.getLocation(p, 1))
01335 
01336     def p_member_const(self, p):
01337         """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
01338         p[0] = ConstMember(type=p[2], name=p[3],
01339                            value=p[5], location=self.getLocation(p, 1),
01340                            doccomments=p.slice[1].doccomments)
01341 
01342 # All "number" products return a function(interface)
01343 
01344     def p_number_decimal(self, p):
01345         """number : NUMBER"""
01346         n = int(p[1])
01347         p[0] = lambda i: n
01348 
01349     def p_number_hex(self, p):
01350         """number : HEXNUM"""
01351         n = int(p[1], 16)
01352         p[0] = lambda i: n
01353 
01354     def p_number_identifier(self, p):
01355         """number : IDENTIFIER"""
01356         id = p[1]
01357         loc = self.getLocation(p, 1)
01358         p[0] = lambda i: i.getConst(id, loc)
01359 
01360     def p_number_paren(self, p):
01361         """number : '(' number ')'"""
01362         p[0] = p[2]
01363 
01364     def p_number_neg(self, p):
01365         """number : '-' number %prec UMINUS"""
01366         n = p[2]
01367         p[0] = lambda i: - n(i)
01368 
01369     def p_number_add(self, p):
01370         """number : number '+' number
01371                   | number '-' number
01372                   | number '*' number"""
01373         n1 = p[1]
01374         n2 = p[3]
01375         if p[2] == '+':
01376             p[0] = lambda i: n1(i) + n2(i)
01377         elif p[2] == '-':
01378             p[0] = lambda i: n1(i) - n2(i)
01379         else:
01380             p[0] = lambda i: n1(i) * n2(i)
01381 
01382     def p_number_shift(self, p):
01383         """number : number LSHIFT number
01384                   | number RSHIFT number"""
01385         n1 = p[1]
01386         n2 = p[3]
01387         if p[2] == '<<':
01388             p[0] = lambda i: n1(i) << n2(i)
01389         else:
01390             p[0] = lambda i: n1(i) >> n2(i)
01391 
01392     def p_number_bitor(self, p):
01393         """number : number '|' number"""
01394         n1 = p[1]
01395         n2 = p[3]
01396         p[0] = lambda i: n1(i) | n2(i)
01397 
01398     def p_member_att(self, p):
01399         """member : attributes optreadonly ATTRIBUTE IDENTIFIER identifier ';'"""
01400         if 'doccomments' in p[1]:
01401             doccomments = p[1]['doccomments']
01402         elif p[2] is not None:
01403             doccomments = p[2]
01404         else:
01405             doccomments = p.slice[3].doccomments
01406 
01407         p[0] = Attribute(type=p[4],
01408                          name=p[5],
01409                          attlist=p[1]['attlist'],
01410                          readonly=p[2] is not None,
01411                          nullable=False,
01412                          defvalue=None,
01413                          location=self.getLocation(p, 3),
01414                          doccomments=doccomments)
01415 
01416     def p_member_method(self, p):
01417         """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
01418         if 'doccomments' in p[1]:
01419             doccomments = p[1]['doccomments']
01420         else:
01421             doccomments = p.slice[2].doccomments
01422 
01423         p[0] = Method(type=p[2],
01424                       name=p[3],
01425                       attlist=p[1]['attlist'],
01426                       paramlist=p[5],
01427                       location=self.getLocation(p, 3),
01428                       doccomments=doccomments,
01429                       raises=p[7])
01430 
01431     def p_paramlist(self, p):
01432         """paramlist : param moreparams
01433                      | """
01434         if len(p) == 1:
01435             p[0] = []
01436         else:
01437             p[0] = list(p[2])
01438             p[0].insert(0, p[1])
01439 
01440     def p_moreparams_start(self, p):
01441         """moreparams :"""
01442         p[0] = []
01443 
01444     def p_moreparams_continue(self, p):
01445         """moreparams : ',' param moreparams"""
01446         p[0] = list(p[3])
01447         p[0].insert(0, p[2])
01448 
01449     def p_param(self, p):
01450         """param : attributes paramtype IDENTIFIER identifier"""
01451         p[0] = Param(paramtype=p[2],
01452                      type=p[3],
01453                      name=p[4],
01454                      attlist=p[1]['attlist'],
01455                      location=self.getLocation(p, 3))
01456 
01457     def p_paramtype(self, p):
01458         """paramtype : IN
01459                      | INOUT
01460                      | OUT"""
01461         p[0] = p[1]
01462 
01463     def p_optreadonly(self, p):
01464         """optreadonly : READONLY
01465                        | """
01466         if len(p) > 1:
01467             p[0] = p.slice[1].doccomments
01468         else:
01469             p[0] = None
01470 
01471     def p_dictionary(self, p):
01472         """dictionary : attributes DICTIONARY IDENTIFIER ifacebase dictbody ';'"""
01473         atts, DICTIONARY, name, base, body, SEMI = p[1:]
01474         attlist = atts['attlist']
01475         doccomments = []
01476         if 'doccomments' in atts:
01477             doccomments.extend(atts['doccomments'])
01478         doccomments.extend(p.slice[2].doccomments)
01479 
01480         l = lambda: self.getLocation(p, 2)
01481 
01482         p[0] = Dictionary(name=name,
01483                           attlist=attlist,
01484                           base=base,
01485                           members=body,
01486                           location=l(),
01487                           doccomments=doccomments)
01488 
01489     def p_dictbody(self, p):
01490         """dictbody : '{' dictmembers '}'
01491                      | """
01492         if len(p) > 1:
01493             p[0] = p[2]
01494 
01495     def p_dictmembers_start(self, p):
01496         """dictmembers : """
01497         p[0] = []
01498 
01499     def p_dictmembers_continue(self, p):
01500         """dictmembers : dictmember dictmembers"""
01501         p[0] = list(p[2])
01502         p[0].insert(0, p[1])
01503 
01504     def p_dictmember(self, p):
01505         """dictmember : attributes IDENTIFIER optnullable IDENTIFIER optdefvalue ';'"""
01506         if 'doccomments' in p[1]:
01507             doccomments = p[1]['doccomments']
01508         else:
01509             doccomments = p.slice[2].doccomments
01510 
01511         p[0] = Attribute(type=p[2],
01512                          name=p[4],
01513                          attlist=p[1]['attlist'],
01514                          readonly=False,
01515                          nullable=p[3] is not None,
01516                          defvalue=p[5],
01517                          location=self.getLocation(p, 1),
01518                          doccomments=doccomments)
01519 
01520     def p_optnullable(self, p):
01521         """optnullable : '?'
01522                        | """
01523         if len(p) > 1:
01524             p[0] = p[1]
01525         else:
01526             p[0] = None
01527 
01528     def p_optdefvalue(self, p):
01529         """optdefvalue : '=' STRING
01530                        | """
01531         if len(p) > 1:
01532             p[0] = p[2]
01533         else:
01534             p[0] = None
01535 
01536     def p_identifier(self, p):
01537         """identifier : DICTIONARY
01538                       | IDENTIFIER"""
01539         p[0] = p[1]
01540 
01541     def p_raises(self, p):
01542         """raises : RAISES '(' idlist ')'
01543                   | """
01544         if len(p) == 1:
01545             p[0] = []
01546         else:
01547             p[0] = p[3]
01548 
01549     def p_idlist(self, p):
01550         """idlist : IDENTIFIER"""
01551         p[0] = [p[1]]
01552 
01553     def p_idlist_continue(self, p):
01554         """idlist : IDENTIFIER ',' idlist"""
01555         p[0] = list(p[3])
01556         p[0].insert(0, p[1])
01557 
01558     def p_error(self, t):
01559         if not t:
01560             raise IDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None)
01561         else:
01562             location = Location(self.lexer, t.lineno, t.lexpos)
01563             raise IDLError("invalid syntax", location)
01564 
01565     def __init__(self, outputdir=''):
01566         self._doccomments = []
01567         self.lexer = lex.lex(object=self,
01568                              outputdir=outputdir,
01569                              lextab='xpidllex',
01570                              optimize=1)
01571         self.parser = yacc.yacc(module=self,
01572                                 outputdir=outputdir,
01573                                 debugfile='xpidl_debug',
01574                                 tabmodule='xpidlyacc',
01575                                 optimize=1)
01576 
01577     def clearComments(self):
01578         self._doccomments = []
01579 
01580     def token(self):
01581         t = self.lexer.token()
01582         if t is not None and t.type != 'CDATA':
01583             t.doccomments = self._doccomments
01584             self._doccomments = []
01585         return t
01586 
01587     def parse(self, data, filename=None):
01588         if filename is not None:
01589             self.lexer.filename = filename
01590         self.lexer.lineno = 1
01591         self.lexer.input(data)
01592         idl = self.parser.parse(lexer=self)
01593         if filename is not None:
01594             idl.deps.append(filename)
01595         return idl
01596 
01597     def getLocation(self, p, i):
01598         return Location(self.lexer, p.lineno(i), p.lexpos(i))
01599 
01600 if __name__ == '__main__':
01601     p = IDLParser()
01602     for f in sys.argv[1:]:
01603         print "Parsing %s" % f
01604         p.parse(open(f).read(), filename=f)