Bug 464146 - about:rights notification sometimes not shown with session restore....
[wine-gecko.git] / xpcom / idl-parser / xpidl.py
blobb23f4b7fa466d8245f282b45a8b75454ea66c72f
1 #!/usr/bin/env python
2 # xpidl.py - A parser for cross-platform IDL (XPIDL) files.
4 # ***** BEGIN LICENSE BLOCK *****
5 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 # The contents of this file are subject to the Mozilla Public License Version
8 # 1.1 (the "License"); you may not use this file except in compliance with
9 # the License. You may obtain a copy of the License at
10 # http://www.mozilla.org/MPL/
12 # Software distributed under the License is distributed on an "AS IS" basis,
13 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 # for the specific language governing rights and limitations under the
15 # License.
17 # The Original Code is mozilla.org code.
19 # The Initial Developer of the Original Code is
20 # Mozilla Foundation.
21 # Portions created by the Initial Developer are Copyright (C) 2008
22 # the Initial Developer. All Rights Reserved.
24 # Contributor(s):
25 # Benjamin Smedberg <benjamin@smedbergs.us>
27 # Alternatively, the contents of this file may be used under the terms of
28 # either of the GNU General Public License Version 2 or later (the "GPL"),
29 # or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 # in which case the provisions of the GPL or the LGPL are applicable instead
31 # of those above. If you wish to allow use of your version of this file only
32 # under the terms of either the GPL or the LGPL, and not to allow others to
33 # use your version of this file under the terms of the MPL, indicate your
34 # decision by deleting the provisions above and replace them with the notice
35 # and other provisions required by the GPL or the LGPL. If you do not delete
36 # the provisions above, a recipient may use your version of this file under
37 # the terms of any one of the MPL, the GPL or the LGPL.
39 # ***** END LICENSE BLOCK *****
41 """A parser for cross-platform IDL (XPIDL) files."""
43 import sys, os.path, re
45 sys.path.append(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir,
46 'other-licenses', 'ply'))
47 from ply import lex, yacc
49 """A type conforms to the following pattern:
51 def isScriptable(self):
52 'returns True or False'
54 def nativeType(self, calltype):
55 'returns a string representation of the native type
56 calltype must be 'in', 'out', or 'inout'
58 Interface members const/method/attribute conform to the following pattern:
60 name = 'string'
62 def toIDL(self):
63 'returns the member signature as IDL'
64 """
66 def attlistToIDL(attlist):
67 if len(attlist) == 0:
68 return ''
70 attlist = list(attlist)
71 attlist.sort(cmp=lambda a,b: cmp(a[0], b[0]))
73 return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
74 for name, value, aloc in attlist])
76 _paramsHardcode = {
77 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
78 3: ('array', 'size_is', 'const'),
81 def paramAttlistToIDL(attlist):
82 if len(attlist) == 0:
83 return ''
85 # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
86 # quirk
87 attlist = list(attlist)
88 sorted = []
89 if len(attlist) in _paramsHardcode:
90 for p in _paramsHardcode[len(attlist)]:
91 i = 0
92 while i < len(attlist):
93 if attlist[i][0] == p:
94 sorted.append(attlist[i])
95 del attlist[i]
96 continue
98 i += 1
100 sorted.extend(attlist)
102 return '[%s] ' % ', '.join(["%s%s" % (name, value is not None and ' (%s)' % value or '')
103 for name, value, aloc in sorted])
105 class BuiltinLocation(object):
106 def get(self):
107 return "<builtin type>"
109 def __str__(self):
110 return self.get()
112 class Builtin(object):
113 kind = 'builtin'
114 location = BuiltinLocation
116 def __init__(self, name, nativename, signed=False, maybeConst=False):
117 self.name = name
118 self.nativename = nativename
119 self.signed = signed
120 self.maybeConst = maybeConst
122 def isScriptable(self):
123 return True
125 def nativeType(self, calltype, shared=False, const=False):
126 if const:
127 print >>sys.stderr, IDLError("[const] doesn't make sense on builtin types.", self.location, warning=True)
128 const = 'const '
129 elif calltype == 'in' and self.nativename.endswith('*'):
130 const = 'const '
131 elif shared:
132 if not self.nativename.endswith('*'):
133 raise IDLError("[shared] not applicable to non-pointer types.", self.location)
134 const = 'const '
135 else:
136 const = ''
137 return "%s%s %s" % (const, self.nativename,
138 calltype != 'in' and '*' or '')
140 builtinNames = [
141 Builtin('boolean', 'PRBool'),
142 Builtin('void', 'void'),
143 Builtin('octet', 'PRUint8'),
144 Builtin('short', 'PRInt16', True, True),
145 Builtin('long', 'PRInt32', True, True),
146 Builtin('long long', 'PRInt64', True, False),
147 Builtin('unsigned short', 'PRUint16', False, True),
148 Builtin('unsigned long', 'PRUint32', False, True),
149 Builtin('unsigned long long', 'PRUint64', False, False),
150 Builtin('float', 'float', True, False),
151 Builtin('double', 'double', True, False),
152 Builtin('char', 'char', True, False),
153 Builtin('string', 'char *', False, False),
154 Builtin('wchar', 'PRUnichar', False, False),
155 Builtin('wstring', 'PRUnichar *', False, False),
158 builtinMap = {}
159 for b in builtinNames:
160 builtinMap[b.name] = b
162 class Location(object):
163 _line = None
165 def __init__(self, lexer, lineno, lexpos):
166 self._lineno = lineno
167 self._lexpos = lexpos
168 self._lexdata = lexer.lexdata
169 self._file = getattr(lexer, 'filename', "<unknown>")
171 def __eq__(self, other):
172 return self._lexpos == other._lexpos and \
173 self._file == other._file
175 def resolve(self):
176 if self._line:
177 return
179 startofline = self._lexdata.rfind('\n', 0, self._lexpos) + 1
180 endofline = self._lexdata.find('\n', self._lexpos, self._lexpos + 80)
181 self._line = self._lexdata[startofline:endofline]
182 self._colno = self._lexpos - startofline
184 def pointerline(self):
185 def i():
186 for i in xrange(0, self._colno):
187 yield " "
188 yield "^"
190 return "".join(i())
192 def get(self):
193 self.resolve()
194 return "%s line %s:%s" % (self._file, self._lineno, self._colno)
196 def __str__(self):
197 self.resolve()
198 return "%s line %s:%s\n%s\n%s" % (self._file, self._lineno, self._colno,
199 self._line, self.pointerline())
201 class NameMap(object):
202 """Map of name -> object. Each object must have a .name and .location property.
203 Setting the same name twice throws an error."""
204 def __init__(self):
205 self._d = {}
207 def __getitem__(self, key):
208 if key in builtinMap:
209 return builtinMap[key]
210 return self._d[key]
212 def __iter__(self):
213 return self._d.itervalues()
215 def __contains__(self, key):
216 return key in builtinMap or key in self._d
218 def set(self, object):
219 if object.name in builtinMap:
220 raise IDLError("name '%s' is a builtin and cannot be redeclared" % (object.name), object.location)
221 if object.name in self._d:
222 old = self._d[object.name]
223 if old == object: return
224 if isinstance(old, Forward) and isinstance(object, Interface):
225 self._d[object.name] = object
226 elif isinstance(old, Interface) and isinstance(object, Forward):
227 pass
228 else:
229 raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name, self._d[object.name].location), object.location)
230 else:
231 self._d[object.name] = object
233 def get(self, id, location):
234 try:
235 return self[id]
236 except KeyError:
237 raise IDLError("Name '%s' not found", location)
239 class IDLError(Exception):
240 def __init__(self, message, location, warning=False):
241 self.message = message
242 self.location = location
243 self.warning = warning
245 def __str__(self):
246 return "%s: %s, %s" % (self.warning and 'warning' or 'error',
247 self.message, self.location)
249 class Include(object):
250 kind = 'include'
252 def __init__(self, filename, location):
253 self.filename = filename
254 self.location = location
256 def __str__(self):
257 return "".join(["include '%s'\n" % self.filename])
259 def resolve(self, parent):
260 def incfiles():
261 yield self.filename
262 for dir in parent.incdirs:
263 yield os.path.join(dir, self.filename)
265 for file in incfiles():
266 if not os.path.exists(file): continue
268 self.IDL = parent.parser.parse(open(file).read(), filename=file)
269 self.IDL.resolve(parent.incdirs, parent.parser)
270 for type in self.IDL.getNames():
271 parent.setName(type)
272 return
274 raise IDLError("File '%s' not found" % self.filename, self.location)
276 class IDL(object):
277 def __init__(self, productions):
278 self.productions = productions
280 def setName(self, object):
281 self.namemap.set(object)
283 def getName(self, id, location):
284 try:
285 return self.namemap[id]
286 except KeyError:
287 raise IDLError("type '%s' not found" % id, location)
289 def hasName(self, id):
290 return id in self.namemap
292 def getNames(self):
293 return iter(self.namemap)
295 def __str__(self):
296 return "".join([str(p) for p in self.productions])
298 def resolve(self, incdirs, parser):
299 self.namemap = NameMap()
300 self.incdirs = incdirs
301 self.parser = parser
302 for p in self.productions:
303 p.resolve(self)
305 def includes(self):
306 for p in self.productions:
307 if p.kind == 'include':
308 yield p
310 class CDATA(object):
311 kind = 'cdata'
312 _re = re.compile(r'\n+')
314 def __init__(self, data, location):
315 self.data = self._re.sub('\n', data)
316 self.location = location
318 def resolve(self, parent):
319 pass
321 def __str__(self):
322 return "cdata: %s\n\t%r\n" % (self.location.get(), self.data)
324 class Typedef(object):
325 kind = 'typedef'
327 def __init__(self, type, name, location, doccomments):
328 self.type = type
329 self.name = name
330 self.location = location
331 self.doccomments = doccomments
333 def __eq__(self, other):
334 return self.name == other.name and self.type == other.type
336 def resolve(self, parent):
337 parent.setName(self)
338 self.realtype = parent.getName(self.type, self.location)
340 def isScriptable(self):
341 return self.realtype.isScriptable()
343 def nativeType(self, calltype):
344 return "%s %s" % (self.name,
345 calltype != 'in' and '*' or '')
347 def __str__(self):
348 return "typedef %s %s\n" % (self.type, self.name)
350 class Forward(object):
351 kind = 'forward'
353 def __init__(self, name, location, doccomments):
354 self.name = name
355 self.location = location
356 self.doccomments = doccomments
358 def __eq__(self, other):
359 return other.kind == 'forward' and other.name == self.name
361 def resolve(self, parent):
362 # Hack alert: if an identifier is already present, move the doccomments
363 # forward.
364 if parent.hasName(self.name):
365 for i in xrange(0, len(parent.productions)):
366 if parent.productions[i] is self: break
367 for i in xrange(i + 1, len(parent.productions)):
368 if hasattr(parent.productions[i], 'doccomments'):
369 parent.productions[i].doccomments[0:0] = self.doccomments
370 break
372 parent.setName(self)
374 def isScriptable(self):
375 return True
377 def nativeType(self, calltype):
378 return "%s %s" % (self.name,
379 calltype != 'in' and '* *' or '*')
381 def __str__(self):
382 return "forward-declared %s\n" % self.name
384 class Native(object):
385 kind = 'native'
387 modifier = None
388 specialtype = None
390 specialtypes = {
391 'nsid': None,
392 'domstring': 'nsAString',
393 'utf8string': 'nsACString',
394 'cstring': 'nsACString',
395 'astring': 'nsAString'
398 def __init__(self, name, nativename, attlist, location):
399 self.name = name
400 self.nativename = nativename
401 self.location = location
403 for name, value, aloc in attlist:
404 if value is not None:
405 raise IDLError("Unexpected attribute value", aloc)
406 if name in ('ptr', 'ref'):
407 if self.modifier is not None:
408 raise IDLError("More than one ptr/ref modifier", aloc)
409 self.modifier = name
410 elif name in self.specialtypes.keys():
411 if self.specialtype is not None:
412 raise IDLError("More than one special type", aloc)
413 self.specialtype = name
414 if self.specialtypes[name] is not None:
415 self.nativename = self.specialtypes[name]
416 else:
417 raise IDLError("Unexpected attribute", aloc)
419 def __eq__(self, other):
420 return self.name == other.name and \
421 self.nativename == other.nativename and \
422 self.modifier == other.modifier and \
423 self.specialtype == other.specialtype
425 def resolve(self, parent):
426 parent.setName(self)
428 def isScriptable(self):
429 if self.specialtype is None:
430 return False
432 if self.specialtype == 'nsid':
433 return self.modifier is not None
435 return self.modifier == 'ref'
437 def nativeType(self, calltype, const=False, shared=False):
438 if shared:
439 if calltype != 'out':
440 raise IDLError("[shared] only applies to out parameters.")
441 const = True
443 if self.specialtype is not None and calltype == 'in':
444 const = True
446 if self.modifier == 'ptr':
447 m = '*' + (calltype != 'in' and '*' or '')
448 elif self.modifier == 'ref':
449 m = '& '
450 else:
451 m = calltype != 'in' and '*' or ''
452 return "%s%s %s" % (const and 'const ' or '', self.nativename, m)
454 def __str__(self):
455 return "native %s(%s)\n" % (self.name, self.nativename)
457 class Interface(object):
458 kind = 'interface'
460 def __init__(self, name, attlist, base, members, location, doccomments):
461 self.name = name
462 self.attributes = InterfaceAttributes(attlist, location)
463 self.base = base
464 self.members = members
465 self.location = location
466 self.namemap = NameMap()
467 self.doccomments = doccomments
468 self.nativename = name
470 for m in members:
471 if not isinstance(m, CDATA):
472 self.namemap.set(m)
474 def __eq__(self, other):
475 return self.name == other.name and self.location == other.location
477 def resolve(self, parent):
478 self.idl = parent
480 # Hack alert: if an identifier is already present, libIDL assigns
481 # doc comments incorrectly. This is quirks-mode extraordinaire!
482 if parent.hasName(self.name):
483 for member in self.members:
484 if hasattr(member, 'doccomments'):
485 member.doccomments[0:0] = self.doccomments
486 break
487 self.doccomments = parent.getName(self.name, None).doccomments
489 parent.setName(self)
490 if self.base is None:
491 if self.name != 'nsISupports':
492 print >>sys.stderr, IDLError("interface '%s' not derived from nsISupports",
493 self.location, warning=True)
494 else:
495 realbase = parent.getName(self.base, self.location)
496 if realbase.kind != 'interface':
497 raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location)
499 if self.attributes.scriptable and not realbase.attributes.scriptable:
500 print >>sys.stderr, IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base), self.location, warning=True)
502 for member in self.members:
503 member.resolve(self)
505 def isScriptable(self):
506 # NOTE: this is not whether *this* interface is scriptable... it's
507 # whether, when used as a type, it's scriptable, which is true of all
508 # interfaces.
509 return True
511 def nativeType(self, calltype, const=False):
512 return "%s%s %s" % (const and 'const ' or '',
513 self.name,
514 calltype != 'in' and '* *' or '*')
516 def __str__(self):
517 l = ["interface %s\n" % self.name]
518 if self.base is not None:
519 l.append("\tbase %s\n" % self.base)
520 l.append(str(self.attributes))
521 if self.members is None:
522 l.append("\tincomplete type\n")
523 else:
524 for m in self.members:
525 l.append(str(m))
526 return "".join(l)
528 def getConst(self, name, location):
529 c = self.namemap.get(name, location)
530 if c.kind != 'const':
531 raise IDLError("symbol '%s' is not a constant", c.location)
533 return c.getValue()
535 class InterfaceAttributes(object):
536 uuid = None
537 scriptable = False
538 function = False
539 deprecated = False
540 noscript = False
542 def setuuid(self, value):
543 self.uuid = value.lower()
545 def setscriptable(self):
546 self.scriptable = True
548 def setfunction(self):
549 self.function = True
551 def setnoscript(self):
552 self.noscript = True
554 def setdeprecated(self):
555 self.deprecated = True
557 actions = {
558 'uuid': (True, setuuid),
559 'scriptable': (False, setscriptable),
560 'function': (False, setfunction),
561 'noscript': (False, setnoscript),
562 'deprecated': (False, setdeprecated),
563 'object': (False, lambda self: True),
566 def __init__(self, attlist, location):
567 def badattribute(self):
568 raise IDLError("Unexpected interface attribute '%s'" % name, location)
570 for name, val, aloc in attlist:
571 hasval, action = self.actions.get(name, (False, badattribute))
572 if hasval:
573 if val is None:
574 raise IDLError("Expected value for attribute '%s'" % name,
575 aloc)
577 action(self, val)
578 else:
579 if val is not None:
580 raise IDLError("Unexpected value for attribute '%s'" % name,
581 aloc)
583 action(self)
585 if self.uuid is None:
586 raise IDLError("interface has no uuid", location)
588 def __str__(self):
589 l = []
590 if self.uuid:
591 l.append("\tuuid: %s\n" % self.uuid)
592 if self.scriptable:
593 l.append("\tscriptable\n")
594 if self.function:
595 l.append("\tfunction\n")
596 return "".join(l)
598 class ConstMember(object):
599 kind = 'const'
600 def __init__(self, type, name, value, location, doccomments):
601 self.type = type
602 self.name = name
603 self.value = value
604 self.location = location
605 self.doccomments = doccomments
607 def resolve(self, parent):
608 self.realtype = parent.idl.getName(self.type, self.location)
609 self.iface = parent
610 basetype = self.realtype
611 while isinstance(basetype, Typedef):
612 basetype = basetype.realtype
613 if not isinstance(basetype, Builtin) or not basetype.maybeConst:
614 raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
616 self.basetype = basetype
618 def getValue(self):
619 return self.value(self.iface)
621 def __str__(self):
622 return "\tconst %s %s = %s\n" % (self.type, self.name, self.getValue())
624 class Attribute(object):
625 kind = 'attribute'
626 noscript = False
627 notxpcom = False
628 readonly = False
629 binaryname = None
631 def __init__(self, type, name, attlist, readonly, location, doccomments):
632 self.type = type
633 self.name = name
634 self.attlist = attlist
635 self.readonly = readonly
636 self.location = location
637 self.doccomments = doccomments
639 for name, value, aloc in attlist:
640 if name == 'binaryname':
641 if value is None:
642 raise IDLError("binaryname attribute requires a value",
643 aloc)
645 self.binaryname = value
646 continue
648 if value is not None:
649 raise IDLError("Unexpected attribute value", aloc)
651 if name == 'noscript':
652 self.noscript = True
653 elif name == 'notxpcom':
654 self.notxpcom = True
655 else:
656 raise IDLError("Unexpected attribute '%s'", aloc)
658 def resolve(self, iface):
659 self.iface = iface
660 self.realtype = iface.idl.getName(self.type, self.location)
662 def toIDL(self):
663 attribs = attlistToIDL(self.attlist)
664 readonly = self.readonly and 'readonly ' or ''
665 return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
667 def isScriptable(self):
668 if not self.iface.attributes.scriptable: return False
669 return not (self.noscript or self.notxpcom)
671 def __str__(self):
672 return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
673 self.type, self.name)
675 class Method(object):
676 kind = 'method'
677 noscript = False
678 notxpcom = False
679 binaryname = None
681 def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
682 self.type = type
683 self.name = name
684 self.attlist = attlist
685 self.params = paramlist
686 self.location = location
687 self.doccomments = doccomments
688 self.raises = raises
690 for name, value, aloc in attlist:
691 if name == 'binaryname':
692 if value is None:
693 raise IDLError("binaryname attribute requires a value",
694 aloc)
696 self.binaryname = value
697 continue
699 if value is not None:
700 raise IDLError("Unexpected attribute value", aloc)
702 if name == 'noscript':
703 self.noscript = True
704 elif name == 'notxpcom':
705 self.notxpcom = True
706 else:
707 raise IDLError("Unexpected attribute '%s'", aloc)
709 self.namemap = NameMap()
710 for p in paramlist:
711 self.namemap.set(p)
713 def resolve(self, iface):
714 self.iface = iface
715 self.realtype = self.iface.idl.getName(self.type, self.location)
716 for p in self.params:
717 p.resolve(self)
719 def isScriptable(self):
720 if not self.iface.attributes.scriptable: return False
721 return not (self.noscript or self.notxpcom)
723 def __str__(self):
724 return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
726 def toIDL(self):
727 if len(self.raises):
728 raises = ' raises (%s)' % ','.join(self.raises)
729 else:
730 raises = ''
732 return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
733 self.type,
734 self.name,
735 ", ".join([p.toIDL()
736 for p in self.params]),
737 raises)
739 class Param(object):
740 size_is = None
741 iid_is = None
742 const = False
743 array = False
744 retval = False
745 shared = False
746 optional = False
748 def __init__(self, paramtype, type, name, attlist, location, realtype=None):
749 self.paramtype = paramtype
750 self.type = type
751 self.name = name
752 self.attlist = attlist
753 self.location = location
754 self.realtype = realtype
756 for name, value, aloc in attlist:
757 # Put the value-taking attributes first!
758 if name == 'size_is':
759 if value is None:
760 raise IDLError("'size_is' must specify a parameter", aloc)
761 self.size_is = value
762 elif name == 'iid_is':
763 if value is None:
764 raise IDLError("'iid_is' must specify a parameter", aloc)
765 self.iid_is = value
766 else:
767 if value is not None:
768 raise IDLError("Unexpected value for attribute '%s'" % name,
769 aloc)
771 if name == 'const':
772 self.const = True
773 elif name == 'array':
774 self.array = True
775 elif name == 'retval':
776 self.retval = True
777 elif name == 'shared':
778 self.shared = True
779 elif name == 'optional':
780 self.optional = True
781 else:
782 raise IDLError("Unexpected attribute '%s'" % name, aloc)
784 def resolve(self, method):
785 self.realtype = method.iface.idl.getName(self.type, self.location)
786 if self.array:
787 self.realtype = Array(self.realtype)
789 def nativeType(self):
790 kwargs = {}
791 if self.shared: kwargs['shared'] = True
792 if self.const: kwargs['const'] = True
794 try:
795 return self.realtype.nativeType(self.paramtype, **kwargs)
796 except IDLError, e:
797 raise IDLError(e.message, self.location)
798 except TypeError, e:
799 raise IDLError("Unexpected parameter attribute", self.location)
801 def toIDL(self):
802 return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
803 self.paramtype,
804 self.type,
805 self.name)
807 class Array(object):
808 def __init__(self, basetype):
809 self.type = basetype
811 def isScriptable(self):
812 return self.type.isScriptable()
814 def nativeType(self, calltype, const=False):
815 return "%s%s*" % (const and 'const ' or '',
816 self.type.nativeType(calltype))
818 class IDLParser(object):
819 keywords = {
820 'const': 'CONST',
821 'interface': 'INTERFACE',
822 'in': 'IN',
823 'inout': 'INOUT',
824 'out': 'OUT',
825 'attribute': 'ATTRIBUTE',
826 'raises': 'RAISES',
827 'readonly': 'READONLY',
828 'native': 'NATIVE',
829 'typedef': 'TYPEDEF'
832 tokens = [
833 'IDENTIFIER',
834 'CDATA',
835 'INCLUDE',
836 'IID',
837 'NUMBER',
838 'HEXNUM',
839 'LSHIFT',
840 'RSHIFT',
841 'NATIVEID',
844 tokens.extend(keywords.values())
846 states = (
847 ('nativeid', 'exclusive'),
850 hexchar = r'[a-fA-F0-9]'
852 t_NUMBER = r'-?\d+'
853 t_HEXNUM = r'0x%s+' % hexchar
854 t_LSHIFT = r'<<'
855 t_RSHIFT= r'>>'
857 literals = '"(){}[],;:=|+-*'
859 t_ignore = ' \t'
861 def t_multilinecomment(self, t):
862 r'/\*(?s).*?\*/'
863 t.lexer.lineno += t.value.count('\n')
864 if t.value.startswith("/**"):
865 self._doccomments.append(t.value)
867 def t_singlelinecomment(self, t):
868 r'(?m)//.*?$'
870 def t_IID(self, t):
871 return t
872 t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
874 def t_IDENTIFIER(self, t):
875 r'unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long|[A-Za-z][A-Za-z_0-9]*'
876 t.type = self.keywords.get(t.value, 'IDENTIFIER')
877 return t
879 def t_LCDATA(self, t):
880 r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
881 t.type = 'CDATA'
882 t.value = t.lexer.lexmatch.group('cdata')
883 t.lexer.lineno += t.value.count('\n')
884 return t
886 def t_INCLUDE(self, t):
887 r'\#include[ \t]+"[^"\n]+"'
888 inc, value, end = t.value.split('"')
889 t.value = value
890 return t
892 def t_directive(self, t):
893 r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
894 print >>sys.stderr, IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
895 Location(lexer=self.lexer,
896 lineno=self.lexer.lineno,
897 lexpos=self.lexer.lexpos))
899 def t_newline(self, t):
900 r'\n+'
901 t.lexer.lineno += len(t.value)
903 def t_nativeid_NATIVEID(self, t):
904 r'[^()\n]+(?=\))'
905 t.lexer.begin('INITIAL')
906 return t
908 t_nativeid_ignore = ''
910 def t_ANY_error(self, t):
911 raise IDLError("unrecognized input",
912 Location(lexer=self.lexer,
913 lineno=self.lexer.lineno,
914 lexpos=self.lexer.lexpos))
916 precedence = (
917 ('left', '|'),
918 ('left', 'LSHIFT', 'RSHIFT'),
919 ('left', '+', '-'),
920 ('left', '*'),
921 ('left', 'UMINUS'),
924 def p_idlfile(self, p):
925 """idlfile : productions"""
926 p[0] = IDL(p[1])
928 def p_productions_start(self, p):
929 """productions : """
930 p[0] = []
932 def p_productions_cdata(self, p):
933 """productions : CDATA productions"""
934 p[0] = list(p[2])
935 p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
937 def p_productions_include(self, p):
938 """productions : INCLUDE productions"""
939 p[0] = list(p[2])
940 p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
942 def p_productions_interface(self, p):
943 """productions : interface productions
944 | typedef productions
945 | native productions"""
946 p[0] = list(p[2])
947 p[0].insert(0, p[1])
949 def p_typedef(self, p):
950 """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
951 p[0] = Typedef(type=p[2],
952 name=p[3],
953 location=self.getLocation(p, 1),
954 doccomments=p.slice[1].doccomments)
956 def p_native(self, p):
957 """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
958 p[0] = Native(name=p[3],
959 nativename=p[6],
960 attlist=p[1]['attlist'],
961 location=self.getLocation(p, 2))
963 def p_afternativeid(self, p):
964 """afternativeid : """
965 # this is a place marker: we switch the lexer into literal identifier
966 # mode here, to slurp up everything until the closeparen
967 self.lexer.begin('nativeid')
969 def p_anyident(self, p):
970 """anyident : IDENTIFIER
971 | CONST"""
972 p[0] = {'value': p[1],
973 'location': self.getLocation(p, 1)}
975 def p_attributes(self, p):
976 """attributes : '[' attlist ']'
977 | """
978 if len(p) == 1:
979 p[0] = {'attlist': []}
980 else:
981 p[0] = {'attlist': p[2],
982 'doccomments': p.slice[1].doccomments}
984 def p_attlist_start(self, p):
985 """attlist : attribute"""
986 p[0] = [p[1]]
988 def p_attlist_continue(self, p):
989 """attlist : attribute ',' attlist"""
990 p[0] = list(p[3])
991 p[0].insert(0, p[1])
993 def p_attribute(self, p):
994 """attribute : anyident attributeval"""
995 p[0] = (p[1]['value'], p[2], p[1]['location'])
997 def p_attributeval(self, p):
998 """attributeval : '(' IDENTIFIER ')'
999 | '(' IID ')'
1000 | """
1001 if len(p) > 1:
1002 p[0] = p[2]
1004 def p_interface(self, p):
1005 """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
1006 atts, INTERFACE, name, base, body, SEMI = p[1:]
1007 attlist = atts['attlist']
1008 doccomments = []
1009 if 'doccomments' in atts:
1010 doccomments.extend(atts['doccomments'])
1011 doccomments.extend(p.slice[2].doccomments)
1013 l = lambda: self.getLocation(p, 2)
1015 if body is None:
1016 # forward-declared interface... must not have attributes!
1017 if len(attlist) != 0:
1018 raise IDLError("Forward-declared interface must not have attributes",
1019 list[0][3])
1021 if base is not None:
1022 raise IDLError("Forward-declared interface must not have a base",
1023 l())
1024 p[0] = Forward(name=name, location=l(), doccomments=doccomments)
1025 else:
1026 p[0] = Interface(name=name,
1027 attlist=attlist,
1028 base=base,
1029 members=body,
1030 location=l(),
1031 doccomments=doccomments)
1033 def p_ifacebody(self, p):
1034 """ifacebody : '{' members '}'
1035 | """
1036 if len(p) > 1:
1037 p[0] = p[2]
1039 def p_ifacebase(self, p):
1040 """ifacebase : ':' IDENTIFIER
1041 | """
1042 if len(p) == 3:
1043 p[0] = p[2]
1045 def p_members_start(self, p):
1046 """members : """
1047 p[0] = []
1049 def p_members_continue(self, p):
1050 """members : member members"""
1051 p[0] = list(p[2])
1052 p[0].insert(0, p[1])
1054 def p_member_cdata(self, p):
1055 """member : CDATA"""
1056 p[0] = CDATA(p[1], self.getLocation(p, 1))
1058 def p_member_const(self, p):
1059 """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
1060 p[0] = ConstMember(type=p[2], name=p[3],
1061 value=p[5], location=self.getLocation(p, 1),
1062 doccomments=p.slice[1].doccomments)
1064 # All "number" products return a function(interface)
1066 def p_number_decimal(self, p):
1067 """number : NUMBER"""
1068 n = int(p[1])
1069 p[0] = lambda i: n
1071 def p_number_hex(self, p):
1072 """number : HEXNUM"""
1073 n = int(p[1], 16)
1074 p[0] = lambda i: n
1076 def p_number_identifier(self, p):
1077 """number : IDENTIFIER"""
1078 id = p[1]
1079 loc = self.getLocation(p, 1)
1080 p[0] = lambda i: i.getConst(id, loc)
1082 def p_number_paren(self, p):
1083 """number : '(' number ')'"""
1084 p[0] = p[2]
1086 def p_number_neg(self, p):
1087 """number : '-' number %prec UMINUS"""
1088 n = p[2]
1089 p[0] = lambda i: - n(i)
1091 def p_number_add(self, p):
1092 """number : number '+' number
1093 | number '-' number
1094 | number '*' number"""
1095 n1 = p[1]
1096 n2 = p[3]
1097 if p[2] == '+':
1098 p[0] = lambda i: n1(i) + n2(i)
1099 elif p[2] == '-':
1100 p[0] = lambda i: n1(i) - n2(i)
1101 else:
1102 p[0] = lambda i: n1(i) * n2(i)
1104 def p_number_shift(self, p):
1105 """number : number LSHIFT number
1106 | number RSHIFT number"""
1107 n1 = p[1]
1108 n2 = p[3]
1109 if p[2] == '<<':
1110 p[0] = lambda i: n1(i) << n2(i)
1111 else:
1112 p[0] = lambda i: n1(i) >> n2(i)
1114 def p_number_bitor(self, p):
1115 """number : number '|' number"""
1116 n1 = p[1]
1117 n2 = p[3]
1118 p[0] = lambda i: n1(i) | n2(i)
1120 def p_member_att(self, p):
1121 """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
1122 if 'doccomments' in p[1]:
1123 doccomments = p[1]['doccomments']
1124 elif p[2] is not None:
1125 doccomments = p[2]
1126 else:
1127 doccomments = p.slice[3].doccomments
1129 p[0] = Attribute(type=p[4],
1130 name=p[5],
1131 attlist=p[1]['attlist'],
1132 readonly=p[2] is not None,
1133 location=self.getLocation(p, 3),
1134 doccomments=doccomments)
1136 def p_member_method(self, p):
1137 """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
1138 if 'doccomments' in p[1]:
1139 doccomments = p[1]['doccomments']
1140 else:
1141 doccomments = p.slice[2].doccomments
1143 p[0] = Method(type=p[2],
1144 name=p[3],
1145 attlist=p[1]['attlist'],
1146 paramlist=p[5],
1147 location=self.getLocation(p, 3),
1148 doccomments=doccomments,
1149 raises=p[7])
1151 def p_paramlist(self, p):
1152 """paramlist : param moreparams
1153 | """
1154 if len(p) == 1:
1155 p[0] = []
1156 else:
1157 p[0] = list(p[2])
1158 p[0].insert(0, p[1])
1160 def p_moreparams_start(self, p):
1161 """moreparams :"""
1162 p[0] = []
1164 def p_moreparams_continue(self, p):
1165 """moreparams : ',' param moreparams"""
1166 p[0] = list(p[3])
1167 p[0].insert(0, p[2])
1169 def p_param(self, p):
1170 """param : attributes paramtype IDENTIFIER IDENTIFIER"""
1171 p[0] = Param(paramtype=p[2],
1172 type=p[3],
1173 name=p[4],
1174 attlist=p[1]['attlist'],
1175 location=self.getLocation(p, 3))
1177 def p_paramtype(self, p):
1178 """paramtype : IN
1179 | INOUT
1180 | OUT"""
1181 p[0] = p[1]
1183 def p_optreadonly(self, p):
1184 """optreadonly : READONLY
1185 | """
1186 if len(p) > 1:
1187 p[0] = p.slice[1].doccomments
1188 else:
1189 p[0] = None
1191 def p_raises(self, p):
1192 """raises : RAISES '(' idlist ')'
1193 | """
1194 if len(p) == 1:
1195 p[0] = []
1196 else:
1197 p[0] = p[3]
1199 def p_idlist(self, p):
1200 """idlist : IDENTIFIER"""
1201 p[0] = [p[1]]
1203 def p_idlist_continue(self, p):
1204 """idlist : IDENTIFIER ',' idlist"""
1205 p[0] = list(p[3])
1206 p[0].insert(0, p[1])
1208 def p_error(self, t):
1209 location = Location(self.lexer, t.lineno, t.lexpos)
1210 raise IDLError("invalid syntax", location)
1212 def __init__(self, outputdir=''):
1213 self._doccomments = []
1214 self.lexer = lex.lex(object=self,
1215 outputdir=outputdir,
1216 lextab='xpidllex',
1217 optimize=1)
1218 self.parser = yacc.yacc(module=self,
1219 outputdir=outputdir,
1220 debugfile='xpidl_debug',
1221 tabmodule='xpidlyacc',
1222 optimize=1)
1224 def clearComments(self):
1225 self._doccomments = []
1227 def token(self):
1228 t = self.lexer.token()
1229 if t is not None and t.type != 'CDATA':
1230 t.doccomments = self._doccomments
1231 self._doccomments = []
1232 return t
1234 def parse(self, data, filename=None):
1235 if filename is not None:
1236 self.lexer.filename = filename
1237 self.lexer.lineno = 1
1238 self.lexer.input(data)
1239 return self.parser.parse(lexer=self)
1241 def getLocation(self, p, i):
1242 return Location(self.lexer, p.lineno(i), p.lexpos(i))
1244 if __name__ == '__main__':
1245 p = IDLParser()
1246 for f in sys.argv[1:]:
1247 print "Parsing %s" % f
1248 p.parse(open(f).read(), filename=f)