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
17 # The Original Code is mozilla.org code.
19 # The Initial Developer of the Original Code is
21 # Portions created by the Initial Developer are Copyright (C) 2008
22 # the Initial Developer. All Rights Reserved.
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:
63 'returns the member signature as IDL'
66 def attlistToIDL(attlist
):
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
])
77 2: ('array', 'shared', 'iid_is', 'size_is', 'retval'),
78 3: ('array', 'size_is', 'const'),
81 def paramAttlistToIDL(attlist
):
85 # Hack alert: g_hash_table_foreach is pretty much unimitatable... hardcode
87 attlist
= list(attlist
)
89 if len(attlist
) in _paramsHardcode
:
90 for p
in _paramsHardcode
[len(attlist
)]:
92 while i
< len(attlist
):
93 if attlist
[i
][0] == p
:
94 sorted.append(attlist
[i
])
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):
107 return "<builtin type>"
112 class Builtin(object):
114 location
= BuiltinLocation
116 def __init__(self
, name
, nativename
, signed
=False, maybeConst
=False):
118 self
.nativename
= nativename
120 self
.maybeConst
= maybeConst
122 def isScriptable(self
):
125 def nativeType(self
, calltype
, shared
=False, const
=False):
127 print >>sys
.stderr
, IDLError("[const] doesn't make sense on builtin types.", self
.location
, warning
=True)
129 elif calltype
== 'in' and self
.nativename
.endswith('*'):
132 if not self
.nativename
.endswith('*'):
133 raise IDLError("[shared] not applicable to non-pointer types.", self
.location
)
137 return "%s%s %s" % (const
, self
.nativename
,
138 calltype
!= 'in' and '*' or '')
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),
159 for b
in builtinNames
:
160 builtinMap
[b
.name
] = b
162 class Location(object):
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
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
):
186 for i
in xrange(0, self
._colno
):
194 return "%s line %s:%s" % (self
._file
, self
._lineno
, self
._colno
)
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."""
207 def __getitem__(self
, key
):
208 if key
in builtinMap
:
209 return builtinMap
[key
]
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
):
229 raise IDLError("name '%s' specified twice. Previous location: %s" % (object.name
, self
._d
[object.name
].location
), object.location
)
231 self
._d
[object.name
] = object
233 def get(self
, id, location
):
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
246 return "%s: %s, %s" % (self
.warning
and 'warning' or 'error',
247 self
.message
, self
.location
)
249 class Include(object):
252 def __init__(self
, filename
, location
):
253 self
.filename
= filename
254 self
.location
= location
257 return "".join(["include '%s'\n" % self
.filename
])
259 def resolve(self
, parent
):
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():
274 raise IDLError("File '%s' not found" % self
.filename
, self
.location
)
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
):
285 return self
.namemap
[id]
287 raise IDLError("type '%s' not found" % id, location
)
289 def hasName(self
, id):
290 return id in self
.namemap
293 return iter(self
.namemap
)
296 return "".join([str(p
) for p
in self
.productions
])
298 def resolve(self
, incdirs
, parser
):
299 self
.namemap
= NameMap()
300 self
.incdirs
= incdirs
302 for p
in self
.productions
:
306 for p
in self
.productions
:
307 if p
.kind
== 'include':
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
):
322 return "cdata: %s\n\t%r\n" % (self
.location
.get(), self
.data
)
324 class Typedef(object):
327 def __init__(self
, type, name
, location
, doccomments
):
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
):
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 '')
348 return "typedef %s %s\n" % (self
.type, self
.name
)
350 class Forward(object):
353 def __init__(self
, name
, location
, doccomments
):
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
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
374 def isScriptable(self
):
377 def nativeType(self
, calltype
):
378 return "%s %s" % (self
.name
,
379 calltype
!= 'in' and '* *' or '*')
382 return "forward-declared %s\n" % self
.name
384 class Native(object):
392 'domstring': 'nsAString',
393 'utf8string': 'nsACString',
394 'cstring': 'nsACString',
395 'astring': 'nsAString'
398 def __init__(self
, name
, nativename
, attlist
, location
):
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
)
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
]
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
):
428 def isScriptable(self
):
429 if self
.specialtype
is None:
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):
439 if calltype
!= 'out':
440 raise IDLError("[shared] only applies to out parameters.")
443 if self
.specialtype
is not None and calltype
== 'in':
446 if self
.modifier
== 'ptr':
447 m
= '*' + (calltype
!= 'in' and '*' or '')
448 elif self
.modifier
== 'ref':
451 m
= calltype
!= 'in' and '*' or ''
452 return "%s%s %s" % (const
and 'const ' or '', self
.nativename
, m
)
455 return "native %s(%s)\n" % (self
.name
, self
.nativename
)
457 class Interface(object):
460 def __init__(self
, name
, attlist
, base
, members
, location
, doccomments
):
462 self
.attributes
= InterfaceAttributes(attlist
, location
)
464 self
.members
= members
465 self
.location
= location
466 self
.namemap
= NameMap()
467 self
.doccomments
= doccomments
468 self
.nativename
= name
471 if not isinstance(m
, CDATA
):
474 def __eq__(self
, other
):
475 return self
.name
== other
.name
and self
.location
== other
.location
477 def resolve(self
, 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
487 self
.doccomments
= parent
.getName(self
.name
, None).doccomments
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)
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
:
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
511 def nativeType(self
, calltype
, const
=False):
512 return "%s%s %s" % (const
and 'const ' or '',
514 calltype
!= 'in' and '* *' or '*')
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")
524 for m
in self
.members
:
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
)
535 class InterfaceAttributes(object):
542 def setuuid(self
, value
):
543 self
.uuid
= value
.lower()
545 def setscriptable(self
):
546 self
.scriptable
= True
548 def setfunction(self
):
551 def setnoscript(self
):
554 def setdeprecated(self
):
555 self
.deprecated
= True
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
))
574 raise IDLError("Expected value for attribute '%s'" % name
,
580 raise IDLError("Unexpected value for attribute '%s'" % name
,
585 if self
.uuid
is None:
586 raise IDLError("interface has no uuid", location
)
591 l
.append("\tuuid: %s\n" % self
.uuid
)
593 l
.append("\tscriptable\n")
595 l
.append("\tfunction\n")
598 class ConstMember(object):
600 def __init__(self
, type, name
, value
, location
, doccomments
):
604 self
.location
= location
605 self
.doccomments
= doccomments
607 def resolve(self
, parent
):
608 self
.realtype
= parent
.idl
.getName(self
.type, self
.location
)
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
619 return self
.value(self
.iface
)
622 return "\tconst %s %s = %s\n" % (self
.type, self
.name
, self
.getValue())
624 class Attribute(object):
631 def __init__(self
, type, name
, attlist
, readonly
, location
, doccomments
):
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':
642 raise IDLError("binaryname attribute requires a value",
645 self
.binaryname
= value
648 if value
is not None:
649 raise IDLError("Unexpected attribute value", aloc
)
651 if name
== 'noscript':
653 elif name
== 'notxpcom':
656 raise IDLError("Unexpected attribute '%s'", aloc
)
658 def resolve(self
, iface
):
660 self
.realtype
= iface
.idl
.getName(self
.type, self
.location
)
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
)
672 return "\t%sattribute %s %s\n" % (self
.readonly
and 'readonly ' or '',
673 self
.type, self
.name
)
675 class Method(object):
681 def __init__(self
, type, name
, attlist
, paramlist
, location
, doccomments
, raises
):
684 self
.attlist
= attlist
685 self
.params
= paramlist
686 self
.location
= location
687 self
.doccomments
= doccomments
690 for name
, value
, aloc
in attlist
:
691 if name
== 'binaryname':
693 raise IDLError("binaryname attribute requires a value",
696 self
.binaryname
= value
699 if value
is not None:
700 raise IDLError("Unexpected attribute value", aloc
)
702 if name
== 'noscript':
704 elif name
== 'notxpcom':
707 raise IDLError("Unexpected attribute '%s'", aloc
)
709 self
.namemap
= NameMap()
713 def resolve(self
, iface
):
715 self
.realtype
= self
.iface
.idl
.getName(self
.type, self
.location
)
716 for p
in self
.params
:
719 def isScriptable(self
):
720 if not self
.iface
.attributes
.scriptable
: return False
721 return not (self
.noscript
or self
.notxpcom
)
724 return "\t%s %s(%s)\n" % (self
.type, self
.name
, ", ".join([p
.name
for p
in self
.params
]))
728 raises
= ' raises (%s)' % ','.join(self
.raises
)
732 return "%s%s %s (%s)%s;" % (attlistToIDL(self
.attlist
),
736 for p
in self
.params
]),
748 def __init__(self
, paramtype
, type, name
, attlist
, location
, realtype
=None):
749 self
.paramtype
= paramtype
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':
760 raise IDLError("'size_is' must specify a parameter", aloc
)
762 elif name
== 'iid_is':
764 raise IDLError("'iid_is' must specify a parameter", aloc
)
767 if value
is not None:
768 raise IDLError("Unexpected value for attribute '%s'" % name
,
773 elif name
== 'array':
775 elif name
== 'retval':
777 elif name
== 'shared':
779 elif name
== 'optional':
782 raise IDLError("Unexpected attribute '%s'" % name
, aloc
)
784 def resolve(self
, method
):
785 self
.realtype
= method
.iface
.idl
.getName(self
.type, self
.location
)
787 self
.realtype
= Array(self
.realtype
)
789 def nativeType(self
):
791 if self
.shared
: kwargs
['shared'] = True
792 if self
.const
: kwargs
['const'] = True
795 return self
.realtype
.nativeType(self
.paramtype
, **kwargs
)
797 raise IDLError(e
.message
, self
.location
)
799 raise IDLError("Unexpected parameter attribute", self
.location
)
802 return "%s%s %s %s" % (paramAttlistToIDL(self
.attlist
),
808 def __init__(self
, 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):
821 'interface': 'INTERFACE',
825 'attribute': 'ATTRIBUTE',
827 'readonly': 'READONLY',
844 tokens
.extend(keywords
.values())
847 ('nativeid', 'exclusive'),
850 hexchar
= r
'[a-fA-F0-9]'
853 t_HEXNUM
= r
'0x%s+' % hexchar
857 literals
= '"(){}[],;:=|+-*'
861 def t_multilinecomment(self
, t
):
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
):
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')
879 def t_LCDATA(self
, t
):
880 r
'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
882 t
.value
= t
.lexer
.lexmatch
.group('cdata')
883 t
.lexer
.lineno
+= t
.value
.count('\n')
886 def t_INCLUDE(self
, t
):
887 r
'\#include[ \t]+"[^"\n]+"'
888 inc
, value
, end
= t
.value
.split('"')
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
):
901 t
.lexer
.lineno
+= len(t
.value
)
903 def t_nativeid_NATIVEID(self
, t
):
905 t
.lexer
.begin('INITIAL')
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
))
918 ('left', 'LSHIFT', 'RSHIFT'),
924 def p_idlfile(self
, p
):
925 """idlfile : productions"""
928 def p_productions_start(self
, p
):
932 def p_productions_cdata(self
, p
):
933 """productions : CDATA productions"""
935 p
[0].insert(0, CDATA(p
[1], self
.getLocation(p
, 1)))
937 def p_productions_include(self
, p
):
938 """productions : INCLUDE productions"""
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"""
949 def p_typedef(self
, p
):
950 """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
951 p
[0] = Typedef(type=p
[2],
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],
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
972 p
[0] = {'value': p
[1],
973 'location': self
.getLocation(p
, 1)}
975 def p_attributes(self
, p
):
976 """attributes : '[' attlist ']'
979 p
[0] = {'attlist': []}
981 p
[0] = {'attlist': p
[2],
982 'doccomments': p
.slice[1].doccomments
}
984 def p_attlist_start(self
, p
):
985 """attlist : attribute"""
988 def p_attlist_continue(self
, p
):
989 """attlist : attribute ',' attlist"""
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 ')'
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']
1009 if 'doccomments' in atts
:
1010 doccomments
.extend(atts
['doccomments'])
1011 doccomments
.extend(p
.slice[2].doccomments
)
1013 l
= lambda: self
.getLocation(p
, 2)
1016 # forward-declared interface... must not have attributes!
1017 if len(attlist
) != 0:
1018 raise IDLError("Forward-declared interface must not have attributes",
1021 if base
is not None:
1022 raise IDLError("Forward-declared interface must not have a base",
1024 p
[0] = Forward(name
=name
, location
=l(), doccomments
=doccomments
)
1026 p
[0] = Interface(name
=name
,
1031 doccomments
=doccomments
)
1033 def p_ifacebody(self
, p
):
1034 """ifacebody : '{' members '}'
1039 def p_ifacebase(self
, p
):
1040 """ifacebase : ':' IDENTIFIER
1045 def p_members_start(self
, p
):
1049 def p_members_continue(self
, p
):
1050 """members : member members"""
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"""
1071 def p_number_hex(self
, p
):
1072 """number : HEXNUM"""
1076 def p_number_identifier(self
, p
):
1077 """number : IDENTIFIER"""
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 ')'"""
1086 def p_number_neg(self
, p
):
1087 """number : '-' number %prec UMINUS"""
1089 p
[0] = lambda i
: - n(i
)
1091 def p_number_add(self
, p
):
1092 """number : number '+' number
1094 | number '*' number"""
1098 p
[0] = lambda i
: n1(i
) + n2(i
)
1100 p
[0] = lambda i
: n1(i
) - n2(i
)
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"""
1110 p
[0] = lambda i
: n1(i
) << n2(i
)
1112 p
[0] = lambda i
: n1(i
) >> n2(i
)
1114 def p_number_bitor(self
, p
):
1115 """number : number '|' number"""
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:
1127 doccomments
= p
.slice[3].doccomments
1129 p
[0] = Attribute(type=p
[4],
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']
1141 doccomments
= p
.slice[2].doccomments
1143 p
[0] = Method(type=p
[2],
1145 attlist
=p
[1]['attlist'],
1147 location
=self
.getLocation(p
, 3),
1148 doccomments
=doccomments
,
1151 def p_paramlist(self
, p
):
1152 """paramlist : param moreparams
1158 p
[0].insert(0, p
[1])
1160 def p_moreparams_start(self
, p
):
1164 def p_moreparams_continue(self
, p
):
1165 """moreparams : ',' param moreparams"""
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],
1174 attlist
=p
[1]['attlist'],
1175 location
=self
.getLocation(p
, 3))
1177 def p_paramtype(self
, p
):
1183 def p_optreadonly(self
, p
):
1184 """optreadonly : READONLY
1187 p
[0] = p
.slice[1].doccomments
1191 def p_raises(self
, p
):
1192 """raises : RAISES '(' idlist ')'
1199 def p_idlist(self
, p
):
1200 """idlist : IDENTIFIER"""
1203 def p_idlist_continue(self
, p
):
1204 """idlist : IDENTIFIER ',' idlist"""
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
,
1218 self
.parser
= yacc
.yacc(module
=self
,
1219 outputdir
=outputdir
,
1220 debugfile
='xpidl_debug',
1221 tabmodule
='xpidlyacc',
1224 def clearComments(self
):
1225 self
._doccomments
= []
1228 t
= self
.lexer
.token()
1229 if t
is not None and t
.type != 'CDATA':
1230 t
.doccomments
= self
._doccomments
1231 self
._doccomments
= []
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__':
1246 for f
in sys
.argv
[1:]:
1247 print "Parsing %s" % f
1248 p
.parse(open(f
).read(), filename
=f
)