michael@0: #!/usr/bin/env python michael@0: # xpidl.py - A parser for cross-platform IDL (XPIDL) files. michael@0: # michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: """A parser for cross-platform IDL (XPIDL) files.""" michael@0: michael@0: import sys, os.path, re michael@0: from ply import lex, yacc michael@0: michael@0: """A type conforms to the following pattern: michael@0: michael@0: def isScriptable(self): michael@0: 'returns True or False' michael@0: michael@0: def nativeType(self, calltype): michael@0: 'returns a string representation of the native type michael@0: calltype must be 'in', 'out', or 'inout' michael@0: michael@0: Interface members const/method/attribute conform to the following pattern: michael@0: michael@0: name = 'string' michael@0: michael@0: def toIDL(self): michael@0: 'returns the member signature as IDL' michael@0: """ michael@0: michael@0: def attlistToIDL(attlist): michael@0: if len(attlist) == 0: michael@0: return '' michael@0: michael@0: attlist = list(attlist) michael@0: attlist.sort(cmp=lambda a,b: cmp(a[0], b[0])) michael@0: michael@0: return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '') michael@0: for name, value, aloc in attlist]) michael@0: michael@0: _paramsHardcode = { michael@0: 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'), michael@0: 3: ('array', 'size_is', 'const'), michael@0: } michael@0: michael@0: def paramAttlistToIDL(attlist): michael@0: if len(attlist) == 0: michael@0: return '' michael@0: michael@0: # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode michael@0: # quirk michael@0: attlist = list(attlist) michael@0: sorted = [] michael@0: if len(attlist) in _paramsHardcode: michael@0: for p in _paramsHardcode[len(attlist)]: michael@0: i = 0 michael@0: while i < len(attlist): michael@0: if attlist[i][0] == p: michael@0: sorted.append(attlist[i]) michael@0: del attlist[i] michael@0: continue michael@0: michael@0: i += 1 michael@0: michael@0: sorted.extend(attlist) michael@0: michael@0: return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '') michael@0: for name, value, aloc in sorted]) michael@0: michael@0: def unaliasType(t): michael@0: while t.kind == 'typedef': michael@0: t = t.realtype michael@0: assert t is not None michael@0: return t michael@0: michael@0: def getBuiltinOrNativeTypeName(t): michael@0: t = unaliasType(t) michael@0: if t.kind == 'builtin': michael@0: return t.name michael@0: elif t.kind == 'native': michael@0: assert t.specialtype is not None michael@0: return '[%s]' % t.specialtype michael@0: else: michael@0: return None michael@0: michael@0: class BuiltinLocation(object): michael@0: def get(self): michael@0: return "" michael@0: michael@0: def __str__(self): michael@0: return self.get() michael@0: michael@0: class Builtin(object): michael@0: kind = 'builtin' michael@0: location = BuiltinLocation michael@0: michael@0: def __init__(self, name, nativename, signed=False, maybeConst=False): michael@0: self.name = name michael@0: self.nativename = nativename michael@0: self.signed = signed michael@0: self.maybeConst = maybeConst michael@0: michael@0: def isScriptable(self): michael@0: return True michael@0: michael@0: def nativeType(self, calltype, shared=False, const=False): michael@0: if const: michael@0: print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True) michael@0: const = 'const ' michael@0: elif calltype == 'in' and self.nativename.endswith('*'): michael@0: const = 'const ' michael@0: elif shared: michael@0: if not self.nativename.endswith('*'): michael@0: raise IDLError("[shared] not applicable to non-pointer types.", self.location) michael@0: const = 'const ' michael@0: else: michael@0: const = '' michael@0: return "%s%s %s" % (const, self.nativename, michael@0: calltype != 'in' and '*' or '') michael@0: michael@0: builtinNames = [ michael@0: Builtin('boolean', 'bool'), michael@0: Builtin('void', 'void'), michael@0: Builtin('octet', 'uint8_t'), michael@0: Builtin('short', 'int16_t', True, True), michael@0: Builtin('long', 'int32_t', True, True), michael@0: Builtin('long long', 'int64_t', True, False), michael@0: Builtin('unsigned short', 'uint16_t', False, True), michael@0: Builtin('unsigned long', 'uint32_t', False, True), michael@0: Builtin('unsigned long long', 'uint64_t', False, False), michael@0: Builtin('float', 'float', True, False), michael@0: Builtin('double', 'double', True, False), michael@0: Builtin('char', 'char', True, False), michael@0: Builtin('string', 'char *', False, False), michael@0: Builtin('wchar', 'char16_t', False, False), michael@0: Builtin('wstring', 'char16_t *', False, False), michael@0: ] michael@0: michael@0: builtinMap = {} michael@0: for b in builtinNames: michael@0: builtinMap[b.name] = b michael@0: michael@0: class Location(object): michael@0: _line = None michael@0: michael@0: def __init__(self, lexer, lineno, lexpos): michael@0: self._lineno = lineno michael@0: self._lexpos = lexpos michael@0: self._lexdata = lexer.lexdata michael@0: self._file = getattr(lexer, 'filename', "") michael@0: michael@0: def __eq__(self, other): michael@0: return self._lexpos == other._lexpos and \ michael@0: self._file == other._file michael@0: michael@0: def resolve(self): michael@0: if self._line: michael@0: return michael@0: michael@0: startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1 michael@0: endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80) michael@0: self._line = self._lexdata[startofline:endofline] michael@0: self._colno = self._lexpos - startofline michael@0: michael@0: def pointerline(self): michael@0: def i(): michael@0: for i in xrange(0, self._colno): michael@0: yield " " michael@0: yield "^" michael@0: michael@0: return "".join(i()) michael@0: michael@0: def get(self): michael@0: self.resolve() michael@0: return "%s line %s:%s" % (self._file, self._lineno, self._colno) michael@0: michael@0: def __str__(self): michael@0: self.resolve() michael@0: return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno, michael@0: self._line, self.pointerline()) michael@0: michael@0: class NameMap(object): michael@0: """Map of name -> object. Each object must have a .name and .location property. michael@0: Setting the same name twice throws an error.""" michael@0: def __init__(self): michael@0: self._d = {} michael@0: michael@0: def __getitem__(self, key): michael@0: if key in builtinMap: michael@0: return builtinMap[key] michael@0: return self._d[key] michael@0: michael@0: def __iter__(self): michael@0: return self._d.itervalues() michael@0: michael@0: def __contains__(self, key): michael@0: return key in builtinMap or key in self._d michael@0: michael@0: def set(self, object): michael@0: if object.name in builtinMap: michael@0: raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location) michael@0: if object.name.startswith("_"): michael@0: object.name = object.name[1:] michael@0: if object.name in self._d: michael@0: old = self._d[object.name] michael@0: if old == object: return michael@0: if isinstance(old, Forward) and isinstance(object, Interface): michael@0: self._d[object.name] = object michael@0: elif isinstance(old, Interface) and isinstance(object, Forward): michael@0: pass michael@0: else: michael@0: raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location) michael@0: else: michael@0: self._d[object.name] = object michael@0: michael@0: def get(self, id, location): michael@0: try: michael@0: return self[id] michael@0: except KeyError: michael@0: raise IDLError("Name '%s' not found", location) michael@0: michael@0: class IDLError(Exception): michael@0: def __init__(self, message, location, warning=False): michael@0: self.message = message michael@0: self.location = location michael@0: self.warning = warning michael@0: michael@0: def __str__(self): michael@0: return "%s: %s, %s" % (self.warning and 'warning' or 'error', michael@0: self.message, self.location) michael@0: michael@0: class Include(object): michael@0: kind = 'include' michael@0: michael@0: def __init__(self, filename, location): michael@0: self.filename = filename michael@0: self.location = location michael@0: michael@0: def __str__(self): michael@0: return "".join(["include '%s'\n" % self.filename]) michael@0: michael@0: def resolve(self, parent): michael@0: def incfiles(): michael@0: yield self.filename michael@0: for dir in parent.incdirs: michael@0: yield os.path.join(dir, self.filename) michael@0: michael@0: for file in incfiles(): michael@0: if not os.path.exists(file): continue michael@0: michael@0: self.IDL = parent.parser.parse(open(file).read(), filename=file) michael@0: self.IDL.resolve(parent.incdirs, parent.parser) michael@0: for type in self.IDL.getNames(): michael@0: parent.setName(type) michael@0: parent.deps.extend(self.IDL.deps) michael@0: return michael@0: michael@0: raise IDLError("File '%s' not found" % self.filename, self.location) michael@0: michael@0: class IDL(object): michael@0: def __init__(self, productions): michael@0: self.productions = productions michael@0: self.deps = [] michael@0: michael@0: def setName(self, object): michael@0: self.namemap.set(object) michael@0: michael@0: def getName(self, id, location): michael@0: try: michael@0: return self.namemap[id] michael@0: except KeyError: michael@0: raise IDLError("type '%s' not found" % id, location) michael@0: michael@0: def hasName(self, id): michael@0: return id in self.namemap michael@0: michael@0: def getNames(self): michael@0: return iter(self.namemap) michael@0: michael@0: def __str__(self): michael@0: return "".join([str(p) for p in self.productions]) michael@0: michael@0: def resolve(self, incdirs, parser): michael@0: self.namemap = NameMap() michael@0: self.incdirs = incdirs michael@0: self.parser = parser michael@0: for p in self.productions: michael@0: p.resolve(self) michael@0: michael@0: def includes(self): michael@0: for p in self.productions: michael@0: if p.kind == 'include': michael@0: yield p michael@0: michael@0: def needsJSTypes(self): michael@0: for p in self.productions: michael@0: if p.kind == 'interface' and p.needsJSTypes(): michael@0: return True michael@0: return False michael@0: michael@0: class CDATA(object): michael@0: kind = 'cdata' michael@0: _re = re.compile(r'\n+') michael@0: michael@0: def __init__(self, data, location): michael@0: self.data = self._re.sub('\n', data) michael@0: self.location = location michael@0: michael@0: def resolve(self, parent): michael@0: pass michael@0: michael@0: def __str__(self): michael@0: return "cdata: %s\n\t%r\n" % (self.location.get(), self.data) michael@0: michael@0: def count(self): michael@0: return 0 michael@0: michael@0: class Typedef(object): michael@0: kind = 'typedef' michael@0: michael@0: def __init__(self, type, name, location, doccomments): michael@0: self.type = type michael@0: self.name = name michael@0: self.location = location michael@0: self.doccomments = doccomments michael@0: michael@0: def __eq__(self, other): michael@0: return self.name == other.name and self.type == other.type michael@0: michael@0: def resolve(self, parent): michael@0: parent.setName(self) michael@0: self.realtype = parent.getName(self.type, self.location) michael@0: michael@0: def isScriptable(self): michael@0: return self.realtype.isScriptable() michael@0: michael@0: def nativeType(self, calltype): michael@0: return "%s %s" % (self.name, michael@0: calltype != 'in' and '*' or '') michael@0: michael@0: def __str__(self): michael@0: return "typedef %s %s\n" % (self.type, self.name) michael@0: michael@0: class Forward(object): michael@0: kind = 'forward' michael@0: michael@0: def __init__(self, name, location, doccomments): michael@0: self.name = name michael@0: self.location = location michael@0: self.doccomments = doccomments michael@0: michael@0: def __eq__(self, other): michael@0: return other.kind == 'forward' and other.name == self.name michael@0: michael@0: def resolve(self, parent): michael@0: # Hack alert: if an identifier is already present, move the doccomments michael@0: # forward. michael@0: if parent.hasName(self.name): michael@0: for i in xrange(0, len(parent.productions)): michael@0: if parent.productions[i] is self: break michael@0: for i in xrange(i + 1, len(parent.productions)): michael@0: if hasattr(parent.productions[i], 'doccomments'): michael@0: parent.productions[i].doccomments[0:0] = self.doccomments michael@0: break michael@0: michael@0: parent.setName(self) michael@0: michael@0: def isScriptable(self): michael@0: return True michael@0: michael@0: def nativeType(self, calltype): michael@0: return "%s %s" % (self.name, michael@0: calltype != 'in' and '* *' or '*') michael@0: michael@0: def __str__(self): michael@0: return "forward-declared %s\n" % self.name michael@0: michael@0: class Native(object): michael@0: kind = 'native' michael@0: michael@0: modifier = None michael@0: specialtype = None michael@0: michael@0: specialtypes = { michael@0: 'nsid': None, michael@0: 'domstring': 'nsAString', michael@0: 'utf8string': 'nsACString', michael@0: 'cstring': 'nsACString', michael@0: 'astring': 'nsAString', michael@0: 'jsval': 'JS::Value' michael@0: } michael@0: michael@0: def __init__(self, name, nativename, attlist, location): michael@0: self.name = name michael@0: self.nativename = nativename michael@0: self.location = location michael@0: michael@0: for name, value, aloc in attlist: michael@0: if value is not None: michael@0: raise IDLError("Unexpected attribute value", aloc) michael@0: if name in ('ptr', 'ref'): michael@0: if self.modifier is not None: michael@0: raise IDLError("More than one ptr/ref modifier", aloc) michael@0: self.modifier = name michael@0: elif name in self.specialtypes.keys(): michael@0: if self.specialtype is not None: michael@0: raise IDLError("More than one special type", aloc) michael@0: self.specialtype = name michael@0: if self.specialtypes[name] is not None: michael@0: self.nativename = self.specialtypes[name] michael@0: else: michael@0: raise IDLError("Unexpected attribute", aloc) michael@0: michael@0: def __eq__(self, other): michael@0: return self.name == other.name and \ michael@0: self.nativename == other.nativename and \ michael@0: self.modifier == other.modifier and \ michael@0: self.specialtype == other.specialtype michael@0: michael@0: def resolve(self, parent): michael@0: parent.setName(self) michael@0: michael@0: def isScriptable(self): michael@0: if self.specialtype is None: michael@0: return False michael@0: michael@0: if self.specialtype == 'nsid': michael@0: return self.modifier is not None michael@0: michael@0: return self.modifier == 'ref' michael@0: michael@0: def isPtr(self, calltype): michael@0: return self.modifier == 'ptr' michael@0: michael@0: def isRef(self, calltype): michael@0: return self.modifier == 'ref' michael@0: michael@0: def nativeType(self, calltype, const=False, shared=False): michael@0: if shared: michael@0: if calltype != 'out': michael@0: raise IDLError("[shared] only applies to out parameters.") michael@0: const = True michael@0: michael@0: if self.specialtype is not None and calltype == 'in': michael@0: const = True michael@0: michael@0: if self.specialtype == 'jsval': michael@0: if calltype == 'out' or calltype == 'inout': michael@0: return "JS::MutableHandleValue " michael@0: return "JS::HandleValue " michael@0: michael@0: if self.isRef(calltype): michael@0: m = '& ' michael@0: elif self.isPtr(calltype): michael@0: m = '*' + ((self.modifier == 'ptr' and calltype != 'in') and '*' or '') michael@0: else: michael@0: m = calltype != 'in' and '*' or '' michael@0: return "%s%s %s" % (const and 'const ' or '', self.nativename, m) michael@0: michael@0: def __str__(self): michael@0: return "native %s(%s)\n" % (self.name, self.nativename) michael@0: michael@0: class Interface(object): michael@0: kind = 'interface' michael@0: michael@0: def __init__(self, name, attlist, base, members, location, doccomments): michael@0: self.name = name michael@0: self.attributes = InterfaceAttributes(attlist, location) michael@0: self.base = base michael@0: self.members = members michael@0: self.location = location michael@0: self.namemap = NameMap() michael@0: self.doccomments = doccomments michael@0: self.nativename = name michael@0: michael@0: for m in members: michael@0: if not isinstance(m, CDATA): michael@0: self.namemap.set(m) michael@0: michael@0: def __eq__(self, other): michael@0: return self.name == other.name and self.location == other.location michael@0: michael@0: def resolve(self, parent): michael@0: self.idl = parent michael@0: michael@0: # Hack alert: if an identifier is already present, libIDL assigns michael@0: # doc comments incorrectly. This is quirks-mode extraordinaire! michael@0: if parent.hasName(self.name): michael@0: for member in self.members: michael@0: if hasattr(member, 'doccomments'): michael@0: member.doccomments[0:0] = self.doccomments michael@0: break michael@0: self.doccomments = parent.getName(self.name, None).doccomments michael@0: michael@0: if self.attributes.function: michael@0: has_method = False michael@0: for member in self.members: michael@0: if member.kind is 'method': michael@0: if has_method: michael@0: raise IDLError("interface '%s' has multiple methods, but marked 'function'" % self.name, self.location) michael@0: else: michael@0: has_method = True michael@0: michael@0: parent.setName(self) michael@0: if self.base is not None: michael@0: realbase = parent.getName(self.base, self.location) michael@0: if realbase.kind != 'interface': michael@0: raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location) michael@0: michael@0: if self.attributes.scriptable and not realbase.attributes.scriptable: michael@0: print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True) michael@0: michael@0: if self.attributes.scriptable and realbase.attributes.builtinclass and not self.attributes.builtinclass: michael@0: raise IDLError("interface '%s' is not builtinclass but derives from builtinclass '%s'" % (self.name, self.base), self.location) michael@0: michael@0: for member in self.members: michael@0: member.resolve(self) michael@0: michael@0: # The number 250 is NOT arbitrary; this number is the maximum number of michael@0: # stub entries defined in xpcom/reflect/xptcall/public/genstubs.pl michael@0: # Do not increase this value without increasing the number in that michael@0: # location, or you WILL cause otherwise unknown problems! michael@0: if self.countEntries() > 250 and not self.attributes.builtinclass: michael@0: raise IDLError("interface '%s' has too many entries" % self.name, michael@0: self.location) michael@0: michael@0: def isScriptable(self): michael@0: # NOTE: this is not whether *this* interface is scriptable... it's michael@0: # whether, when used as a type, it's scriptable, which is true of all michael@0: # interfaces. michael@0: return True michael@0: michael@0: def nativeType(self, calltype, const=False): michael@0: return "%s%s %s" % (const and 'const ' or '', michael@0: self.name, michael@0: calltype != 'in' and '* *' or '*') michael@0: michael@0: def __str__(self): michael@0: l = ["interface %s\n" % self.name] michael@0: if self.base is not None: michael@0: l.append("\tbase %s\n" % self.base) michael@0: l.append(str(self.attributes)) michael@0: if self.members is None: michael@0: l.append("\tincomplete type\n") michael@0: else: michael@0: for m in self.members: michael@0: l.append(str(m)) michael@0: return "".join(l) michael@0: michael@0: def getConst(self, name, location): michael@0: # The constant may be in a base class michael@0: iface = self michael@0: while name not in iface.namemap and iface is not None: michael@0: iface = self.idl.getName(self.base, self.location) michael@0: if iface is None: michael@0: raise IDLError("cannot find symbol '%s'" % name, c.location) michael@0: c = iface.namemap.get(name, location) michael@0: if c.kind != 'const': michael@0: raise IDLError("symbol '%s' is not a constant", c.location) michael@0: michael@0: return c.getValue() michael@0: michael@0: def needsJSTypes(self): michael@0: for m in self.members: michael@0: if m.kind == "attribute" and m.type == "jsval": michael@0: return True michael@0: if m.kind == "method" and m.needsJSTypes(): michael@0: return True michael@0: return False michael@0: michael@0: def countEntries(self): michael@0: ''' Returns the number of entries in the vtable for this interface. ''' michael@0: total = sum(member.count() for member in self.members) michael@0: if self.base is not None: michael@0: realbase = self.idl.getName(self.base, self.location) michael@0: total += realbase.countEntries() michael@0: return total michael@0: michael@0: class InterfaceAttributes(object): michael@0: uuid = None michael@0: scriptable = False michael@0: builtinclass = False michael@0: function = False michael@0: deprecated = False michael@0: noscript = False michael@0: michael@0: def setuuid(self, value): michael@0: self.uuid = value.lower() michael@0: michael@0: def setscriptable(self): michael@0: self.scriptable = True michael@0: michael@0: def setfunction(self): michael@0: self.function = True michael@0: michael@0: def setnoscript(self): michael@0: self.noscript = True michael@0: michael@0: def setbuiltinclass(self): michael@0: self.builtinclass = True michael@0: michael@0: def setdeprecated(self): michael@0: self.deprecated = True michael@0: michael@0: actions = { michael@0: 'uuid': (True, setuuid), michael@0: 'scriptable': (False, setscriptable), michael@0: 'builtinclass': (False, setbuiltinclass), michael@0: 'function': (False, setfunction), michael@0: 'noscript': (False, setnoscript), michael@0: 'deprecated': (False, setdeprecated), michael@0: 'object': (False, lambda self: True), michael@0: } michael@0: michael@0: def __init__(self, attlist, location): michael@0: def badattribute(self): michael@0: raise IDLError("Unexpected interface attribute '%s'" % name, location) michael@0: michael@0: for name, val, aloc in attlist: michael@0: hasval, action = self.actions.get(name, (False, badattribute)) michael@0: if hasval: michael@0: if val is None: michael@0: raise IDLError("Expected value for attribute '%s'" % name, michael@0: aloc) michael@0: michael@0: action(self, val) michael@0: else: michael@0: if val is not None: michael@0: raise IDLError("Unexpected value for attribute '%s'" % name, michael@0: aloc) michael@0: michael@0: action(self) michael@0: michael@0: if self.uuid is None: michael@0: raise IDLError("interface has no uuid", location) michael@0: michael@0: def __str__(self): michael@0: l = [] michael@0: if self.uuid: michael@0: l.append("\tuuid: %s\n" % self.uuid) michael@0: if self.scriptable: michael@0: l.append("\tscriptable\n") michael@0: if self.builtinclass: michael@0: l.append("\tbuiltinclass\n") michael@0: if self.function: michael@0: l.append("\tfunction\n") michael@0: return "".join(l) michael@0: michael@0: class ConstMember(object): michael@0: kind = 'const' michael@0: def __init__(self, type, name, value, location, doccomments): michael@0: self.type = type michael@0: self.name = name michael@0: self.value = value michael@0: self.location = location michael@0: self.doccomments = doccomments michael@0: michael@0: def resolve(self, parent): michael@0: self.realtype = parent.idl.getName(self.type, self.location) michael@0: self.iface = parent michael@0: basetype = self.realtype michael@0: while isinstance(basetype, Typedef): michael@0: basetype = basetype.realtype michael@0: if not isinstance(basetype, Builtin) or not basetype.maybeConst: michael@0: raise IDLError("const may only be a short or long type, not %s" % self.type, self.location) michael@0: michael@0: self.basetype = basetype michael@0: michael@0: def getValue(self): michael@0: return self.value(self.iface) michael@0: michael@0: def __str__(self): michael@0: return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue()) michael@0: michael@0: def count(self): michael@0: return 0 michael@0: michael@0: class Attribute(object): michael@0: kind = 'attribute' michael@0: noscript = False michael@0: readonly = False michael@0: implicit_jscontext = False michael@0: nostdcall = False michael@0: binaryname = None michael@0: null = None michael@0: undefined = None michael@0: deprecated = False michael@0: infallible = False michael@0: michael@0: def __init__(self, type, name, attlist, readonly, location, doccomments): michael@0: self.type = type michael@0: self.name = name michael@0: self.attlist = attlist michael@0: self.readonly = readonly michael@0: self.location = location michael@0: self.doccomments = doccomments michael@0: michael@0: for name, value, aloc in attlist: michael@0: if name == 'binaryname': michael@0: if value is None: michael@0: raise IDLError("binaryname attribute requires a value", michael@0: aloc) michael@0: michael@0: self.binaryname = value michael@0: continue michael@0: michael@0: if name == 'Null': michael@0: if value is None: michael@0: raise IDLError("'Null' attribute requires a value", aloc) michael@0: if readonly: michael@0: raise IDLError("'Null' attribute only makes sense for setters", michael@0: aloc); michael@0: if value not in ('Empty', 'Null', 'Stringify'): michael@0: raise IDLError("'Null' attribute value must be 'Empty', 'Null' or 'Stringify'", michael@0: aloc); michael@0: self.null = value michael@0: elif name == 'Undefined': michael@0: if value is None: michael@0: raise IDLError("'Undefined' attribute requires a value", aloc) michael@0: if readonly: michael@0: raise IDLError("'Undefined' attribute only makes sense for setters", michael@0: aloc); michael@0: if value not in ('Empty', 'Null'): michael@0: raise IDLError("'Undefined' attribute value must be 'Empty' or 'Null'", michael@0: aloc); michael@0: self.undefined = value michael@0: else: michael@0: if value is not None: michael@0: raise IDLError("Unexpected attribute value", aloc) michael@0: michael@0: if name == 'noscript': michael@0: self.noscript = True michael@0: elif name == 'implicit_jscontext': michael@0: self.implicit_jscontext = True michael@0: elif name == 'deprecated': michael@0: self.deprecated = True michael@0: elif name == 'nostdcall': michael@0: self.nostdcall = True michael@0: elif name == 'infallible': michael@0: self.infallible = True michael@0: else: michael@0: raise IDLError("Unexpected attribute '%s'" % name, aloc) michael@0: michael@0: def resolve(self, iface): michael@0: self.iface = iface michael@0: self.realtype = iface.idl.getName(self.type, self.location) michael@0: if (self.null is not None and michael@0: getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'): michael@0: raise IDLError("'Null' attribute can only be used on DOMString", michael@0: self.location) michael@0: if (self.undefined is not None and michael@0: getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'): michael@0: raise IDLError("'Undefined' attribute can only be used on DOMString", michael@0: self.location) michael@0: if self.infallible and not self.realtype.kind == 'builtin': michael@0: raise IDLError('[infallible] only works on builtin types ' michael@0: '(numbers, booleans, and raw char types)', michael@0: self.location) michael@0: if self.infallible and not iface.attributes.builtinclass: michael@0: raise IDLError('[infallible] attributes are only allowed on ' michael@0: '[builtinclass] interfaces', michael@0: self.location) michael@0: michael@0: michael@0: def toIDL(self): michael@0: attribs = attlistToIDL(self.attlist) michael@0: readonly = self.readonly and 'readonly ' or '' michael@0: return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name) michael@0: michael@0: def isScriptable(self): michael@0: if not self.iface.attributes.scriptable: return False michael@0: return not self.noscript michael@0: michael@0: def __str__(self): michael@0: return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '', michael@0: self.type, self.name) michael@0: michael@0: def count(self): michael@0: return self.readonly and 1 or 2 michael@0: michael@0: class Method(object): michael@0: kind = 'method' michael@0: noscript = False michael@0: notxpcom = False michael@0: binaryname = None michael@0: implicit_jscontext = False michael@0: nostdcall = False michael@0: optional_argc = False michael@0: deprecated = False michael@0: michael@0: def __init__(self, type, name, attlist, paramlist, location, doccomments, raises): michael@0: self.type = type michael@0: self.name = name michael@0: self.attlist = attlist michael@0: self.params = paramlist michael@0: self.location = location michael@0: self.doccomments = doccomments michael@0: self.raises = raises michael@0: michael@0: for name, value, aloc in attlist: michael@0: if name == 'binaryname': michael@0: if value is None: michael@0: raise IDLError("binaryname attribute requires a value", michael@0: aloc) michael@0: michael@0: self.binaryname = value michael@0: continue michael@0: michael@0: if value is not None: michael@0: raise IDLError("Unexpected attribute value", aloc) michael@0: michael@0: if name == 'noscript': michael@0: self.noscript = True michael@0: elif name == 'notxpcom': michael@0: self.notxpcom = True michael@0: elif name == 'implicit_jscontext': michael@0: self.implicit_jscontext = True michael@0: elif name == 'optional_argc': michael@0: self.optional_argc = True michael@0: elif name == 'deprecated': michael@0: self.deprecated = True michael@0: elif name == 'nostdcall': michael@0: self.nostdcall = True michael@0: else: michael@0: raise IDLError("Unexpected attribute '%s'" % name, aloc) michael@0: michael@0: self.namemap = NameMap() michael@0: for p in paramlist: michael@0: self.namemap.set(p) michael@0: michael@0: def resolve(self, iface): michael@0: self.iface = iface michael@0: self.realtype = self.iface.idl.getName(self.type, self.location) michael@0: for p in self.params: michael@0: p.resolve(self) michael@0: for p in self.params: michael@0: if p.retval and p != self.params[-1]: michael@0: raise IDLError("'retval' parameter '%s' is not the last parameter" % p.name, self.location) michael@0: if p.size_is: michael@0: found_size_param = False michael@0: for size_param in self.params: michael@0: if p.size_is == size_param.name: michael@0: found_size_param = True michael@0: if getBuiltinOrNativeTypeName(size_param.realtype) != 'unsigned long': michael@0: raise IDLError("is_size parameter must have type 'unsigned long'", self.location) michael@0: if not found_size_param: michael@0: raise IDLError("could not find is_size parameter '%s'" % p.size_is, self.location) michael@0: michael@0: def isScriptable(self): michael@0: if not self.iface.attributes.scriptable: return False michael@0: return not (self.noscript or self.notxpcom) michael@0: michael@0: def __str__(self): michael@0: return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params])) michael@0: michael@0: def toIDL(self): michael@0: if len(self.raises): michael@0: raises = ' raises (%s)' % ','.join(self.raises) michael@0: else: michael@0: raises = '' michael@0: michael@0: return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist), michael@0: self.type, michael@0: self.name, michael@0: ", ".join([p.toIDL() michael@0: for p in self.params]), michael@0: raises) michael@0: michael@0: def needsJSTypes(self): michael@0: if self.implicit_jscontext: michael@0: return True michael@0: if self.type == "jsval": michael@0: return True michael@0: for p in self.params: michael@0: t = p.realtype michael@0: if isinstance(t, Native) and t.specialtype == "jsval": michael@0: return True michael@0: return False michael@0: michael@0: def count(self): michael@0: return 1 michael@0: michael@0: class Param(object): michael@0: size_is = None michael@0: iid_is = None michael@0: const = False michael@0: array = False michael@0: retval = False michael@0: shared = False michael@0: optional = False michael@0: null = None michael@0: undefined = None michael@0: michael@0: def __init__(self, paramtype, type, name, attlist, location, realtype=None): michael@0: self.paramtype = paramtype michael@0: self.type = type michael@0: self.name = name michael@0: self.attlist = attlist michael@0: self.location = location michael@0: self.realtype = realtype michael@0: michael@0: for name, value, aloc in attlist: michael@0: # Put the value-taking attributes first! michael@0: if name == 'size_is': michael@0: if value is None: michael@0: raise IDLError("'size_is' must specify a parameter", aloc) michael@0: self.size_is = value michael@0: elif name == 'iid_is': michael@0: if value is None: michael@0: raise IDLError("'iid_is' must specify a parameter", aloc) michael@0: self.iid_is = value michael@0: elif name == 'Null': michael@0: if value is None: michael@0: raise IDLError("'Null' must specify a parameter", aloc) michael@0: if value not in ('Empty', 'Null', 'Stringify'): michael@0: raise IDLError("'Null' parameter value must be 'Empty', 'Null', or 'Stringify'", michael@0: aloc); michael@0: self.null = value michael@0: elif name == 'Undefined': michael@0: if value is None: michael@0: raise IDLError("'Undefined' must specify a parameter", aloc) michael@0: if value not in ('Empty', 'Null'): michael@0: raise IDLError("'Undefined' parameter value must be 'Empty' or 'Null'", michael@0: aloc); michael@0: self.undefined = value michael@0: else: michael@0: if value is not None: michael@0: raise IDLError("Unexpected value for attribute '%s'" % name, michael@0: aloc) michael@0: michael@0: if name == 'const': michael@0: self.const = True michael@0: elif name == 'array': michael@0: self.array = True michael@0: elif name == 'retval': michael@0: self.retval = True michael@0: elif name == 'shared': michael@0: self.shared = True michael@0: elif name == 'optional': michael@0: self.optional = True michael@0: else: michael@0: raise IDLError("Unexpected attribute '%s'" % name, aloc) michael@0: michael@0: def resolve(self, method): michael@0: self.realtype = method.iface.idl.getName(self.type, self.location) michael@0: if self.array: michael@0: self.realtype = Array(self.realtype) michael@0: if (self.null is not None and michael@0: getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'): michael@0: raise IDLError("'Null' attribute can only be used on DOMString", michael@0: self.location) michael@0: if (self.undefined is not None and michael@0: getBuiltinOrNativeTypeName(self.realtype) != '[domstring]'): michael@0: raise IDLError("'Undefined' attribute can only be used on DOMString", michael@0: self.location) michael@0: michael@0: def nativeType(self): michael@0: kwargs = {} michael@0: if self.shared: kwargs['shared'] = True michael@0: if self.const: kwargs['const'] = True michael@0: michael@0: try: michael@0: return self.realtype.nativeType(self.paramtype, **kwargs) michael@0: except IDLError, e: michael@0: raise IDLError(e.message, self.location) michael@0: except TypeError, e: michael@0: raise IDLError("Unexpected parameter attribute", self.location) michael@0: michael@0: def toIDL(self): michael@0: return "%s%s %s %s" % (paramAttlistToIDL(self.attlist), michael@0: self.paramtype, michael@0: self.type, michael@0: self.name) michael@0: michael@0: class Array(object): michael@0: def __init__(self, basetype): michael@0: self.type = basetype michael@0: michael@0: def isScriptable(self): michael@0: return self.type.isScriptable() michael@0: michael@0: def nativeType(self, calltype, const=False): michael@0: return "%s%s*" % (const and 'const ' or '', michael@0: self.type.nativeType(calltype)) michael@0: michael@0: class IDLParser(object): michael@0: keywords = { michael@0: 'const': 'CONST', michael@0: 'interface': 'INTERFACE', michael@0: 'in': 'IN', michael@0: 'inout': 'INOUT', michael@0: 'out': 'OUT', michael@0: 'attribute': 'ATTRIBUTE', michael@0: 'raises': 'RAISES', michael@0: 'readonly': 'READONLY', michael@0: 'native': 'NATIVE', michael@0: 'typedef': 'TYPEDEF', michael@0: 'Infinity': 'INFINITY' michael@0: } michael@0: michael@0: tokens = [ michael@0: 'IDENTIFIER', michael@0: 'CDATA', michael@0: 'INCLUDE', michael@0: 'IID', michael@0: 'NUMBER', michael@0: 'HEXNUM', michael@0: 'LSHIFT', michael@0: 'RSHIFT', michael@0: 'NATIVEID', michael@0: ] michael@0: michael@0: tokens.extend(keywords.values()) michael@0: michael@0: states = ( michael@0: ('nativeid', 'exclusive'), michael@0: ) michael@0: michael@0: hexchar = r'[a-fA-F0-9]' michael@0: michael@0: t_NUMBER = r'-?\d+' michael@0: t_HEXNUM = r'0x%s+' % hexchar michael@0: t_LSHIFT = r'<<' michael@0: t_RSHIFT= r'>>' michael@0: michael@0: literals = '"(){}[],;:=|+-*' michael@0: michael@0: t_ignore = ' \t' michael@0: michael@0: def t_multilinecomment(self, t): michael@0: r'/\*(?s).*?\*/' michael@0: t.lexer.lineno += t.value.count('\n') michael@0: if t.value.startswith("/**"): michael@0: self._doccomments.append(t.value) michael@0: michael@0: def t_singlelinecomment(self, t): michael@0: r'(?m)//.*?$' michael@0: michael@0: def t_IID(self, t): michael@0: return t michael@0: t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar} michael@0: michael@0: def t_IDENTIFIER(self, t): michael@0: 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: t.type = self.keywords.get(t.value, 'IDENTIFIER') michael@0: return t michael@0: michael@0: def t_LCDATA(self, t): michael@0: r'(?s)%\{[ ]*C\+\+[ ]*\n(?P.*?\n?)%\}[ ]*(C\+\+)?' michael@0: t.type = 'CDATA' michael@0: t.value = t.lexer.lexmatch.group('cdata') michael@0: t.lexer.lineno += t.value.count('\n') michael@0: return t michael@0: michael@0: def t_INCLUDE(self, t): michael@0: r'\#include[ \t]+"[^"\n]+"' michael@0: inc, value, end = t.value.split('"') michael@0: t.value = value michael@0: return t michael@0: michael@0: def t_directive(self, t): michael@0: r'\#(?P[a-zA-Z]+)[^\n]+' michael@0: raise IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'), michael@0: Location(lexer=self.lexer, lineno=self.lexer.lineno, michael@0: lexpos=self.lexer.lexpos)) michael@0: michael@0: def t_newline(self, t): michael@0: r'\n+' michael@0: t.lexer.lineno += len(t.value) michael@0: michael@0: def t_nativeid_NATIVEID(self, t): michael@0: r'[^()\n]+(?=\))' michael@0: t.lexer.begin('INITIAL') michael@0: return t michael@0: michael@0: t_nativeid_ignore = '' michael@0: michael@0: def t_ANY_error(self, t): michael@0: raise IDLError("unrecognized input", michael@0: Location(lexer=self.lexer, michael@0: lineno=self.lexer.lineno, michael@0: lexpos=self.lexer.lexpos)) michael@0: michael@0: precedence = ( michael@0: ('left', '|'), michael@0: ('left', 'LSHIFT', 'RSHIFT'), michael@0: ('left', '+', '-'), michael@0: ('left', '*'), michael@0: ('left', 'UMINUS'), michael@0: ) michael@0: michael@0: def p_idlfile(self, p): michael@0: """idlfile : productions""" michael@0: p[0] = IDL(p[1]) michael@0: michael@0: def p_productions_start(self, p): michael@0: """productions : """ michael@0: p[0] = [] michael@0: michael@0: def p_productions_cdata(self, p): michael@0: """productions : CDATA productions""" michael@0: p[0] = list(p[2]) michael@0: p[0].insert(0, CDATA(p[1], self.getLocation(p, 1))) michael@0: michael@0: def p_productions_include(self, p): michael@0: """productions : INCLUDE productions""" michael@0: p[0] = list(p[2]) michael@0: p[0].insert(0, Include(p[1], self.getLocation(p, 1))) michael@0: michael@0: def p_productions_interface(self, p): michael@0: """productions : interface productions michael@0: | typedef productions michael@0: | native productions""" michael@0: p[0] = list(p[2]) michael@0: p[0].insert(0, p[1]) michael@0: michael@0: def p_typedef(self, p): michael@0: """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'""" michael@0: p[0] = Typedef(type=p[2], michael@0: name=p[3], michael@0: location=self.getLocation(p, 1), michael@0: doccomments=p.slice[1].doccomments) michael@0: michael@0: def p_native(self, p): michael@0: """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'""" michael@0: p[0] = Native(name=p[3], michael@0: nativename=p[6], michael@0: attlist=p[1]['attlist'], michael@0: location=self.getLocation(p, 2)) michael@0: michael@0: def p_afternativeid(self, p): michael@0: """afternativeid : """ michael@0: # this is a place marker: we switch the lexer into literal identifier michael@0: # mode here, to slurp up everything until the closeparen michael@0: self.lexer.begin('nativeid') michael@0: michael@0: def p_anyident(self, p): michael@0: """anyident : IDENTIFIER michael@0: | CONST""" michael@0: p[0] = {'value': p[1], michael@0: 'location': self.getLocation(p, 1)} michael@0: michael@0: def p_attributes(self, p): michael@0: """attributes : '[' attlist ']' michael@0: | """ michael@0: if len(p) == 1: michael@0: p[0] = {'attlist': []} michael@0: else: michael@0: p[0] = {'attlist': p[2], michael@0: 'doccomments': p.slice[1].doccomments} michael@0: michael@0: def p_attlist_start(self, p): michael@0: """attlist : attribute""" michael@0: p[0] = [p[1]] michael@0: michael@0: def p_attlist_continue(self, p): michael@0: """attlist : attribute ',' attlist""" michael@0: p[0] = list(p[3]) michael@0: p[0].insert(0, p[1]) michael@0: michael@0: def p_attribute(self, p): michael@0: """attribute : anyident attributeval""" michael@0: p[0] = (p[1]['value'], p[2], p[1]['location']) michael@0: michael@0: def p_attributeval(self, p): michael@0: """attributeval : '(' IDENTIFIER ')' michael@0: | '(' IID ')' michael@0: | """ michael@0: if len(p) > 1: michael@0: p[0] = p[2] michael@0: michael@0: def p_interface(self, p): michael@0: """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'""" michael@0: atts, INTERFACE, name, base, body, SEMI = p[1:] michael@0: attlist = atts['attlist'] michael@0: doccomments = [] michael@0: if 'doccomments' in atts: michael@0: doccomments.extend(atts['doccomments']) michael@0: doccomments.extend(p.slice[2].doccomments) michael@0: michael@0: l = lambda: self.getLocation(p, 2) michael@0: michael@0: if body is None: michael@0: # forward-declared interface... must not have attributes! michael@0: if len(attlist) != 0: michael@0: raise IDLError("Forward-declared interface must not have attributes", michael@0: list[0][3]) michael@0: michael@0: if base is not None: michael@0: raise IDLError("Forward-declared interface must not have a base", michael@0: l()) michael@0: p[0] = Forward(name=name, location=l(), doccomments=doccomments) michael@0: else: michael@0: p[0] = Interface(name=name, michael@0: attlist=attlist, michael@0: base=base, michael@0: members=body, michael@0: location=l(), michael@0: doccomments=doccomments) michael@0: michael@0: def p_ifacebody(self, p): michael@0: """ifacebody : '{' members '}' michael@0: | """ michael@0: if len(p) > 1: michael@0: p[0] = p[2] michael@0: michael@0: def p_ifacebase(self, p): michael@0: """ifacebase : ':' IDENTIFIER michael@0: | """ michael@0: if len(p) == 3: michael@0: p[0] = p[2] michael@0: michael@0: def p_members_start(self, p): michael@0: """members : """ michael@0: p[0] = [] michael@0: michael@0: def p_members_continue(self, p): michael@0: """members : member members""" michael@0: p[0] = list(p[2]) michael@0: p[0].insert(0, p[1]) michael@0: michael@0: def p_member_cdata(self, p): michael@0: """member : CDATA""" michael@0: p[0] = CDATA(p[1], self.getLocation(p, 1)) michael@0: michael@0: def p_member_const(self, p): michael@0: """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """ michael@0: p[0] = ConstMember(type=p[2], name=p[3], michael@0: value=p[5], location=self.getLocation(p, 1), michael@0: doccomments=p.slice[1].doccomments) michael@0: michael@0: # All "number" products return a function(interface) michael@0: michael@0: def p_number_decimal(self, p): michael@0: """number : NUMBER""" michael@0: n = int(p[1]) michael@0: p[0] = lambda i: n michael@0: michael@0: def p_number_hex(self, p): michael@0: """number : HEXNUM""" michael@0: n = int(p[1], 16) michael@0: p[0] = lambda i: n michael@0: michael@0: def p_number_identifier(self, p): michael@0: """number : IDENTIFIER""" michael@0: id = p[1] michael@0: loc = self.getLocation(p, 1) michael@0: p[0] = lambda i: i.getConst(id, loc) michael@0: michael@0: def p_number_paren(self, p): michael@0: """number : '(' number ')'""" michael@0: p[0] = p[2] michael@0: michael@0: def p_number_neg(self, p): michael@0: """number : '-' number %prec UMINUS""" michael@0: n = p[2] michael@0: p[0] = lambda i: - n(i) michael@0: michael@0: def p_number_add(self, p): michael@0: """number : number '+' number michael@0: | number '-' number michael@0: | number '*' number""" michael@0: n1 = p[1] michael@0: n2 = p[3] michael@0: if p[2] == '+': michael@0: p[0] = lambda i: n1(i) + n2(i) michael@0: elif p[2] == '-': michael@0: p[0] = lambda i: n1(i) - n2(i) michael@0: else: michael@0: p[0] = lambda i: n1(i) * n2(i) michael@0: michael@0: def p_number_shift(self, p): michael@0: """number : number LSHIFT number michael@0: | number RSHIFT number""" michael@0: n1 = p[1] michael@0: n2 = p[3] michael@0: if p[2] == '<<': michael@0: p[0] = lambda i: n1(i) << n2(i) michael@0: else: michael@0: p[0] = lambda i: n1(i) >> n2(i) michael@0: michael@0: def p_number_bitor(self, p): michael@0: """number : number '|' number""" michael@0: n1 = p[1] michael@0: n2 = p[3] michael@0: p[0] = lambda i: n1(i) | n2(i) michael@0: michael@0: def p_member_att(self, p): michael@0: """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'""" michael@0: if 'doccomments' in p[1]: michael@0: doccomments = p[1]['doccomments'] michael@0: elif p[2] is not None: michael@0: doccomments = p[2] michael@0: else: michael@0: doccomments = p.slice[3].doccomments michael@0: michael@0: p[0] = Attribute(type=p[4], michael@0: name=p[5], michael@0: attlist=p[1]['attlist'], michael@0: readonly=p[2] is not None, michael@0: location=self.getLocation(p, 3), michael@0: doccomments=doccomments) michael@0: michael@0: def p_member_method(self, p): michael@0: """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'""" michael@0: if 'doccomments' in p[1]: michael@0: doccomments = p[1]['doccomments'] michael@0: else: michael@0: doccomments = p.slice[2].doccomments michael@0: michael@0: p[0] = Method(type=p[2], michael@0: name=p[3], michael@0: attlist=p[1]['attlist'], michael@0: paramlist=p[5], michael@0: location=self.getLocation(p, 3), michael@0: doccomments=doccomments, michael@0: raises=p[7]) michael@0: michael@0: def p_paramlist(self, p): michael@0: """paramlist : param moreparams michael@0: | """ michael@0: if len(p) == 1: michael@0: p[0] = [] michael@0: else: michael@0: p[0] = list(p[2]) michael@0: p[0].insert(0, p[1]) michael@0: michael@0: def p_moreparams_start(self, p): michael@0: """moreparams :""" michael@0: p[0] = [] michael@0: michael@0: def p_moreparams_continue(self, p): michael@0: """moreparams : ',' param moreparams""" michael@0: p[0] = list(p[3]) michael@0: p[0].insert(0, p[2]) michael@0: michael@0: def p_param(self, p): michael@0: """param : attributes paramtype IDENTIFIER IDENTIFIER""" michael@0: p[0] = Param(paramtype=p[2], michael@0: type=p[3], michael@0: name=p[4], michael@0: attlist=p[1]['attlist'], michael@0: location=self.getLocation(p, 3)) michael@0: michael@0: def p_paramtype(self, p): michael@0: """paramtype : IN michael@0: | INOUT michael@0: | OUT""" michael@0: p[0] = p[1] michael@0: michael@0: def p_optreadonly(self, p): michael@0: """optreadonly : READONLY michael@0: | """ michael@0: if len(p) > 1: michael@0: p[0] = p.slice[1].doccomments michael@0: else: michael@0: p[0] = None michael@0: michael@0: def p_raises(self, p): michael@0: """raises : RAISES '(' idlist ')' michael@0: | """ michael@0: if len(p) == 1: michael@0: p[0] = [] michael@0: else: michael@0: p[0] = p[3] michael@0: michael@0: def p_idlist(self, p): michael@0: """idlist : IDENTIFIER""" michael@0: p[0] = [p[1]] michael@0: michael@0: def p_idlist_continue(self, p): michael@0: """idlist : IDENTIFIER ',' idlist""" michael@0: p[0] = list(p[3]) michael@0: p[0].insert(0, p[1]) michael@0: michael@0: def p_error(self, t): michael@0: if not t: michael@0: raise IDLError("Syntax Error at end of file. Possibly due to missing semicolon(;), braces(}) or both", None) michael@0: else: michael@0: location = Location(self.lexer, t.lineno, t.lexpos) michael@0: raise IDLError("invalid syntax", location) michael@0: michael@0: def __init__(self, outputdir=''): michael@0: self._doccomments = [] michael@0: self.lexer = lex.lex(object=self, michael@0: outputdir=outputdir, michael@0: lextab='xpidllex', michael@0: optimize=1) michael@0: self.parser = yacc.yacc(module=self, michael@0: outputdir=outputdir, michael@0: debug=0, michael@0: tabmodule='xpidlyacc', michael@0: optimize=1) michael@0: michael@0: def clearComments(self): michael@0: self._doccomments = [] michael@0: michael@0: def token(self): michael@0: t = self.lexer.token() michael@0: if t is not None and t.type != 'CDATA': michael@0: t.doccomments = self._doccomments michael@0: self._doccomments = [] michael@0: return t michael@0: michael@0: def parse(self, data, filename=None): michael@0: if filename is not None: michael@0: self.lexer.filename = filename michael@0: self.lexer.lineno = 1 michael@0: self.lexer.input(data) michael@0: idl = self.parser.parse(lexer=self) michael@0: if filename is not None: michael@0: idl.deps.append(filename) michael@0: return idl michael@0: michael@0: def getLocation(self, p, i): michael@0: return Location(self.lexer, p.lineno(i), p.lexpos(i)) michael@0: michael@0: if __name__ == '__main__': michael@0: p = IDLParser() michael@0: for f in sys.argv[1:]: michael@0: print "Parsing %s" % f michael@0: p.parse(open(f).read(), filename=f)