Bug 455527 - In some cases Embedding startup crashes if gAppData == null. r=benjamin.
[mozilla-central.git] / xpcom / idl-parser / xpidl.py
blob9fbd6daf641c254eac823d8075aac18f6ea4569f
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
541 def setuuid(self, value):
542 self.uuid = value.lower()
544 def setscriptable(self):
545 self.scriptable = True
547 def setfunction(self):
548 self.function = True
550 def setnoscript(self):
551 self.noscript = True
553 def setdeprecated(self):
554 self.deprecated = True
556 actions = {
557 'uuid': (True, setuuid),
558 'scriptable': (False, setscriptable),
559 'function': (False, setfunction),
560 'noscript': (False, setnoscript),
561 'deprecated': (False, setdeprecated),
562 'object': (False, lambda self: True),
565 def __init__(self, attlist, location):
566 def badattribute(self):
567 raise IDLError("Unexpected interface attribute '%s'" % name, location)
569 for name, val, aloc in attlist:
570 hasval, action = self.actions.get(name, (False, badattribute))
571 if hasval:
572 if val is None:
573 raise IDLError("Expected value for attribute '%s'" % name,
574 aloc)
576 action(self, val)
577 else:
578 if val is not None:
579 raise IDLError("Unexpected value for attribute '%s'" % name,
580 aloc)
582 action(self)
584 if self.uuid is None:
585 raise IDLError("interface has no uuid", location)
587 def __str__(self):
588 l = []
589 if self.uuid:
590 l.append("\tuuid: %s\n" % self.uuid)
591 if self.scriptable:
592 l.append("\tscriptable\n")
593 if self.function:
594 l.append("\tfunction\n")
595 return "".join(l)
597 class ConstMember(object):
598 kind = 'const'
599 def __init__(self, type, name, value, location, doccomments):
600 self.type = type
601 self.name = name
602 self.value = value
603 self.location = location
604 self.doccomments = doccomments
606 def resolve(self, parent):
607 self.realtype = parent.idl.getName(self.type, self.location)
608 self.iface = parent
609 basetype = self.realtype
610 while isinstance(basetype, Typedef):
611 basetype = basetype.realtype
612 if not isinstance(basetype, Builtin) or not basetype.maybeConst:
613 raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
615 self.basetype = basetype
617 def getValue(self):
618 return self.value(self.iface)
620 class Attribute(object):
621 kind = 'attribute'
622 noscript = False
623 notxpcom = False
625 def __init__(self, type, name, attlist, readonly, location, doccomments):
626 self.type = type
627 self.name = name
628 self.attlist = attlist
629 self.readonly = readonly
630 self.location = location
631 self.binaryname = None
632 self.doccomments = doccomments
634 for name, value, aloc in attlist:
635 if name == 'binaryname':
636 if value is None:
637 raise IDLError("binaryname attribute requires a value",
638 aloc)
640 self.binaryname = value
641 continue
643 if value is not None:
644 raise IDLError("Unexpected attribute value", aloc)
646 if name == 'noscript':
647 self.noscript = True
648 elif name == 'notxpcom':
649 self.notxpcom = True
650 else:
651 raise IDLError("Unexpected attribute '%s'", aloc)
653 def resolve(self, iface):
654 self.iface = iface
655 self.realtype = iface.idl.getName(self.type, self.location)
657 def toIDL(self):
658 attribs = attlistToIDL(self.attlist)
659 readonly = self.readonly and 'readonly ' or ''
660 return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
662 def isScriptable(self):
663 if not self.iface.attributes.scriptable: return False
664 return not (self.noscript or self.notxpcom)
666 def __str__(self):
667 return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
668 self.type, self.name)
670 class Method(object):
671 kind = 'method'
672 noscript = False
673 notxpcom = False
674 binaryname = None
676 def __init__(self, type, name, attlist, paramlist, location, doccomments, raises):
677 self.type = type
678 self.name = name
679 self.attlist = attlist
680 self.params = paramlist
681 self.location = location
682 self.doccomments = doccomments
683 self.raises = raises
685 for name, value, aloc in attlist:
686 if name == 'binaryname':
687 if value is None:
688 raise IDLError("binaryname attribute requires a value",
689 aloc)
691 self.binaryname = value
692 continue
694 if value is not None:
695 raise IDLError("Unexpected attribute value", aloc)
697 if name == 'noscript':
698 self.noscript = True
699 elif name == 'notxpcom':
700 self.notxpcom = True
701 else:
702 raise IDLError("Unexpected attribute '%s'", aloc)
704 self.namemap = NameMap()
705 for p in paramlist:
706 self.namemap.set(p)
708 def resolve(self, iface):
709 self.iface = iface
710 self.realtype = self.iface.idl.getName(self.type, self.location)
711 for p in self.params:
712 p.resolve(self)
714 def isScriptable(self):
715 if not self.iface.attributes.scriptable: return False
716 return not (self.noscript or self.notxpcom)
718 def __str__(self):
719 return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
721 def toIDL(self):
722 if len(self.raises):
723 raises = ' raises (%s)' % ','.join(self.raises)
724 else:
725 raises = ''
727 return "%s%s %s (%s)%s;" % (attlistToIDL(self.attlist),
728 self.type,
729 self.name,
730 ", ".join([p.toIDL()
731 for p in self.params]),
732 raises)
734 class Param(object):
735 size_is = None
736 iid_is = None
737 const = False
738 array = False
739 retval = False
740 shared = False
741 optional = False
743 def __init__(self, paramtype, type, name, attlist, location, realtype=None):
744 self.paramtype = paramtype
745 self.type = type
746 self.name = name
747 self.attlist = attlist
748 self.location = location
749 self.realtype = realtype
751 for name, value, aloc in attlist:
752 # Put the value-taking attributes first!
753 if name == 'size_is':
754 if value is None:
755 raise IDLError("'size_is' must specify a parameter", aloc)
756 self.size_is = value
757 elif name == 'iid_is':
758 if value is None:
759 raise IDLError("'iid_is' must specify a parameter", aloc)
760 self.iid_is = value
761 else:
762 if value is not None:
763 raise IDLError("Unexpected value for attribute '%s'" % name,
764 aloc)
766 if name == 'const':
767 self.const = True
768 elif name == 'array':
769 self.array = True
770 elif name == 'retval':
771 self.retval = True
772 elif name == 'shared':
773 self.shared = True
774 elif name == 'optional':
775 self.optional = True
776 else:
777 raise IDLError("Unexpected attribute '%s'" % name, aloc)
779 def resolve(self, method):
780 self.realtype = method.iface.idl.getName(self.type, self.location)
781 if self.array:
782 self.realtype = Array(self.realtype)
784 def nativeType(self):
785 kwargs = {}
786 if self.shared: kwargs['shared'] = True
787 if self.const: kwargs['const'] = True
789 try:
790 return self.realtype.nativeType(self.paramtype, **kwargs)
791 except IDLError, e:
792 raise IDLError(e.message, self.location)
793 except TypeError, e:
794 raise IDLError("Unexpected parameter attribute", self.location)
796 def toIDL(self):
797 return "%s%s %s %s" % (paramAttlistToIDL(self.attlist),
798 self.paramtype,
799 self.type,
800 self.name)
802 class Array(object):
803 def __init__(self, basetype):
804 self.type = basetype
806 def isScriptable(self):
807 return self.type.isScriptable()
809 def nativeType(self, calltype, const=False):
810 return "%s%s*" % (const and 'const ' or '',
811 self.type.nativeType(calltype))
813 class IDLParser(object):
814 keywords = {
815 'const': 'CONST',
816 'interface': 'INTERFACE',
817 'in': 'IN',
818 'inout': 'INOUT',
819 'out': 'OUT',
820 'attribute': 'ATTRIBUTE',
821 'raises': 'RAISES',
822 'readonly': 'READONLY',
823 'native': 'NATIVE',
824 'typedef': 'TYPEDEF'
827 tokens = [
828 'IDENTIFIER',
829 'CDATA',
830 'INCLUDE',
831 'IID',
832 'NUMBER',
833 'HEXNUM',
834 'LSHIFT',
835 'RSHIFT',
836 'NATIVEID',
839 tokens.extend(keywords.values())
841 states = (
842 ('nativeid', 'exclusive'),
845 hexchar = r'[a-fA-F0-9]'
847 t_NUMBER = r'-?\d+'
848 t_HEXNUM = r'0x%s+' % hexchar
849 t_LSHIFT = r'<<'
850 t_RSHIFT= r'>>'
852 literals = '"(){}[],;:=|+-*'
854 t_ignore = ' \t'
856 def t_multilinecomment(self, t):
857 r'/\*(?s).*?\*/'
858 t.lexer.lineno += t.value.count('\n')
859 if t.value.startswith("/**"):
860 self._doccomments.append(t.value)
862 def t_singlelinecomment(self, t):
863 r'(?m)//.*?$'
865 def t_IID(self, t):
866 return t
867 t_IID.__doc__ = r'%(c)s{8}-%(c)s{4}-%(c)s{4}-%(c)s{4}-%(c)s{12}' % {'c': hexchar}
869 def t_IDENTIFIER(self, t):
870 r'unsigned\ long\ long|unsigned\ short|unsigned\ long|long\ long|[A-Za-z][A-Za-z_0-9]*'
871 t.type = self.keywords.get(t.value, 'IDENTIFIER')
872 return t
874 def t_LCDATA(self, t):
875 r'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
876 t.type = 'CDATA'
877 t.value = t.lexer.lexmatch.group('cdata')
878 t.lexer.lineno += t.value.count('\n')
879 return t
881 def t_INCLUDE(self, t):
882 r'\#include[ \t]+"[^"\n]+"'
883 inc, value, end = t.value.split('"')
884 t.value = value
885 return t
887 def t_directive(self, t):
888 r'\#(?P<directive>[a-zA-Z]+)[^\n]+'
889 print >>sys.stderr, IDLError("Unrecognized directive %s" % t.lexer.lexmatch.group('directive'),
890 Location(lexer=self.lexer,
891 lineno=self.lexer.lineno,
892 lexpos=self.lexer.lexpos))
894 def t_newline(self, t):
895 r'\n+'
896 t.lexer.lineno += len(t.value)
898 def t_nativeid_NATIVEID(self, t):
899 r'[^()\n]+(?=\))'
900 t.lexer.begin('INITIAL')
901 return t
903 t_nativeid_ignore = ''
905 def t_ANY_error(self, t):
906 raise IDLError("unrecognized input",
907 Location(lexer=self.lexer,
908 lineno=self.lexer.lineno,
909 lexpos=self.lexer.lexpos))
911 precedence = (
912 ('left', '|'),
913 ('left', 'LSHIFT', 'RSHIFT'),
914 ('left', '+', '-'),
915 ('left', '*'),
916 ('left', 'UMINUS'),
919 def p_idlfile(self, p):
920 """idlfile : productions"""
921 p[0] = IDL(p[1])
923 def p_productions_start(self, p):
924 """productions : """
925 p[0] = []
927 def p_productions_cdata(self, p):
928 """productions : CDATA productions"""
929 p[0] = list(p[2])
930 p[0].insert(0, CDATA(p[1], self.getLocation(p, 1)))
932 def p_productions_include(self, p):
933 """productions : INCLUDE productions"""
934 p[0] = list(p[2])
935 p[0].insert(0, Include(p[1], self.getLocation(p, 1)))
937 def p_productions_interface(self, p):
938 """productions : interface productions
939 | typedef productions
940 | native productions"""
941 p[0] = list(p[2])
942 p[0].insert(0, p[1])
944 def p_typedef(self, p):
945 """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
946 p[0] = Typedef(type=p[2],
947 name=p[3],
948 location=self.getLocation(p, 1),
949 doccomments=p.slice[1].doccomments)
951 def p_native(self, p):
952 """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
953 p[0] = Native(name=p[3],
954 nativename=p[6],
955 attlist=p[1]['attlist'],
956 location=self.getLocation(p, 2))
958 def p_afternativeid(self, p):
959 """afternativeid : """
960 # this is a place marker: we switch the lexer into literal identifier
961 # mode here, to slurp up everything until the closeparen
962 self.lexer.begin('nativeid')
964 def p_anyident(self, p):
965 """anyident : IDENTIFIER
966 | CONST"""
967 p[0] = {'value': p[1],
968 'location': self.getLocation(p, 1)}
970 def p_attributes(self, p):
971 """attributes : '[' attlist ']'
972 | """
973 if len(p) == 1:
974 p[0] = {'attlist': []}
975 else:
976 p[0] = {'attlist': p[2],
977 'doccomments': p.slice[1].doccomments}
979 def p_attlist_start(self, p):
980 """attlist : attribute"""
981 p[0] = [p[1]]
983 def p_attlist_continue(self, p):
984 """attlist : attribute ',' attlist"""
985 p[0] = list(p[3])
986 p[0].insert(0, p[1])
988 def p_attribute(self, p):
989 """attribute : anyident attributeval"""
990 p[0] = (p[1]['value'], p[2], p[1]['location'])
992 def p_attributeval(self, p):
993 """attributeval : '(' IDENTIFIER ')'
994 | '(' IID ')'
995 | """
996 if len(p) > 1:
997 p[0] = p[2]
999 def p_interface(self, p):
1000 """interface : attributes INTERFACE IDENTIFIER ifacebase ifacebody ';'"""
1001 atts, INTERFACE, name, base, body, SEMI = p[1:]
1002 attlist = atts['attlist']
1003 doccomments = []
1004 if 'doccomments' in atts:
1005 doccomments.extend(atts['doccomments'])
1006 doccomments.extend(p.slice[2].doccomments)
1008 l = lambda: self.getLocation(p, 2)
1010 if body is None:
1011 # forward-declared interface... must not have attributes!
1012 if len(attlist) != 0:
1013 raise IDLError("Forward-declared interface must not have attributes",
1014 list[0][3])
1016 if base is not None:
1017 raise IDLError("Forward-declared interface must not have a base",
1018 l())
1019 p[0] = Forward(name=name, location=l(), doccomments=doccomments)
1020 else:
1021 p[0] = Interface(name=name,
1022 attlist=attlist,
1023 base=base,
1024 members=body,
1025 location=l(),
1026 doccomments=doccomments)
1028 def p_ifacebody(self, p):
1029 """ifacebody : '{' members '}'
1030 | """
1031 if len(p) > 1:
1032 p[0] = p[2]
1034 def p_ifacebase(self, p):
1035 """ifacebase : ':' IDENTIFIER
1036 | """
1037 if len(p) == 3:
1038 p[0] = p[2]
1040 def p_members_start(self, p):
1041 """members : """
1042 p[0] = []
1044 def p_members_continue(self, p):
1045 """members : member members"""
1046 p[0] = list(p[2])
1047 p[0].insert(0, p[1])
1049 def p_member_cdata(self, p):
1050 """member : CDATA"""
1051 p[0] = CDATA(p[1], self.getLocation(p, 1))
1053 def p_member_const(self, p):
1054 """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
1055 p[0] = ConstMember(type=p[2], name=p[3],
1056 value=p[5], location=self.getLocation(p, 1),
1057 doccomments=p.slice[1].doccomments)
1059 # All "number" products return a function(interface)
1061 def p_number_decimal(self, p):
1062 """number : NUMBER"""
1063 n = int(p[1])
1064 p[0] = lambda i: n
1066 def p_number_hex(self, p):
1067 """number : HEXNUM"""
1068 n = int(p[1], 16)
1069 p[0] = lambda i: n
1071 def p_number_identifier(self, p):
1072 """number : IDENTIFIER"""
1073 id = p[1]
1074 loc = self.getLocation(p, 1)
1075 p[0] = lambda i: i.getConst(id, loc)
1077 def p_number_paren(self, p):
1078 """number : '(' number ')'"""
1079 p[0] = p[2]
1081 def p_number_neg(self, p):
1082 """number : '-' number %prec UMINUS"""
1083 n = p[2]
1084 p[0] = lambda i: - n(i)
1086 def p_number_add(self, p):
1087 """number : number '+' number
1088 | number '-' number
1089 | number '*' number"""
1090 n1 = p[1]
1091 n2 = p[3]
1092 if p[2] == '+':
1093 p[0] = lambda i: n1(i) + n2(i)
1094 elif p[2] == '-':
1095 p[0] = lambda i: n1(i) - n2(i)
1096 else:
1097 p[0] = lambda i: n1(i) * n2(i)
1099 def p_number_shift(self, p):
1100 """number : number LSHIFT number
1101 | number RSHIFT number"""
1102 n1 = p[1]
1103 n2 = p[3]
1104 if p[2] == '<<':
1105 p[0] = lambda i: n1(i) << n2(i)
1106 else:
1107 p[0] = lambda i: n1(i) >> n2(i)
1109 def p_number_bitor(self, p):
1110 """number : number '|' number"""
1111 n1 = p[1]
1112 n2 = p[3]
1113 p[0] = lambda i: n1(i) | n2(i)
1115 def p_member_att(self, p):
1116 """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
1117 if 'doccomments' in p[1]:
1118 doccomments = p[1]['doccomments']
1119 elif p[2] is not None:
1120 doccomments = p[2]
1121 else:
1122 doccomments = p.slice[3].doccomments
1124 p[0] = Attribute(type=p[4],
1125 name=p[5],
1126 attlist=p[1]['attlist'],
1127 readonly=p[2] is not None,
1128 location=self.getLocation(p, 1),
1129 doccomments=doccomments)
1131 def p_member_method(self, p):
1132 """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
1133 if 'doccomments' in p[1]:
1134 doccomments = p[1]['doccomments']
1135 else:
1136 doccomments = p.slice[2].doccomments
1138 p[0] = Method(type=p[2],
1139 name=p[3],
1140 attlist=p[1]['attlist'],
1141 paramlist=p[5],
1142 location=self.getLocation(p, 1),
1143 doccomments=doccomments,
1144 raises=p[7])
1146 def p_paramlist(self, p):
1147 """paramlist : param moreparams
1148 | """
1149 if len(p) == 1:
1150 p[0] = []
1151 else:
1152 p[0] = list(p[2])
1153 p[0].insert(0, p[1])
1155 def p_moreparams_start(self, p):
1156 """moreparams :"""
1157 p[0] = []
1159 def p_moreparams_continue(self, p):
1160 """moreparams : ',' param moreparams"""
1161 p[0] = list(p[3])
1162 p[0].insert(0, p[2])
1164 def p_param(self, p):
1165 """param : attributes paramtype IDENTIFIER IDENTIFIER"""
1166 p[0] = Param(paramtype=p[2],
1167 type=p[3],
1168 name=p[4],
1169 attlist=p[1]['attlist'],
1170 location=self.getLocation(p, 3))
1172 def p_paramtype(self, p):
1173 """paramtype : IN
1174 | INOUT
1175 | OUT"""
1176 p[0] = p[1]
1178 def p_optreadonly(self, p):
1179 """optreadonly : READONLY
1180 | """
1181 if len(p) > 1:
1182 p[0] = p.slice[1].doccomments
1183 else:
1184 p[0] = None
1186 def p_raises(self, p):
1187 """raises : RAISES '(' idlist ')'
1188 | """
1189 if len(p) == 1:
1190 p[0] = []
1191 else:
1192 p[0] = p[3]
1194 def p_idlist(self, p):
1195 """idlist : IDENTIFIER"""
1196 p[0] = [p[1]]
1198 def p_idlist_continue(self, p):
1199 """idlist : IDENTIFIER ',' idlist"""
1200 p[0] = list(p[3])
1201 p[0].insert(0, p[1])
1203 def p_error(self, t):
1204 location = Location(self.lexer, t.lineno, t.lexpos)
1205 raise IDLError("invalid syntax", location)
1207 def __init__(self, outputdir=''):
1208 self._doccomments = []
1209 self.lexer = lex.lex(object=self,
1210 outputdir=outputdir,
1211 lextab='xpidllex',
1212 optimize=1)
1213 self.parser = yacc.yacc(module=self,
1214 outputdir=outputdir,
1215 debugfile='xpidl_debug',
1216 tabmodule='xpidlyacc',
1217 optimize=1)
1219 def clearComments(self):
1220 self._doccomments = []
1222 def token(self):
1223 t = self.lexer.token()
1224 if t is not None and t.type != 'CDATA':
1225 t.doccomments = self._doccomments
1226 self._doccomments = []
1227 return t
1229 def parse(self, data, filename=None):
1230 if filename is not None:
1231 self.lexer.filename = filename
1232 self.lexer.lineno = 1
1233 self.lexer.input(data)
1234 return self.parser.parse(lexer=self)
1236 def getLocation(self, p, i):
1237 return Location(self.lexer, p.lineno(i), p.lexpos(i))
1239 if __name__ == '__main__':
1240 p = IDLParser()
1241 for f in sys.argv[1:]:
1242 print "Parsing %s" % f
1243 p.parse(open(f).read(), filename=f)