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):
541 def setuuid(self
, value
):
542 self
.uuid
= value
.lower()
544 def setscriptable(self
):
545 self
.scriptable
= True
547 def setfunction(self
):
550 def setnoscript(self
):
553 def setdeprecated(self
):
554 self
.deprecated
= True
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
))
573 raise IDLError("Expected value for attribute '%s'" % name
,
579 raise IDLError("Unexpected value for attribute '%s'" % name
,
584 if self
.uuid
is None:
585 raise IDLError("interface has no uuid", location
)
590 l
.append("\tuuid: %s\n" % self
.uuid
)
592 l
.append("\tscriptable\n")
594 l
.append("\tfunction\n")
597 class ConstMember(object):
599 def __init__(self
, type, name
, value
, location
, doccomments
):
603 self
.location
= location
604 self
.doccomments
= doccomments
606 def resolve(self
, parent
):
607 self
.realtype
= parent
.idl
.getName(self
.type, self
.location
)
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
618 return self
.value(self
.iface
)
620 class Attribute(object):
625 def __init__(self
, type, name
, attlist
, readonly
, location
, doccomments
):
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':
637 raise IDLError("binaryname attribute requires a value",
640 self
.binaryname
= value
643 if value
is not None:
644 raise IDLError("Unexpected attribute value", aloc
)
646 if name
== 'noscript':
648 elif name
== 'notxpcom':
651 raise IDLError("Unexpected attribute '%s'", aloc
)
653 def resolve(self
, iface
):
655 self
.realtype
= iface
.idl
.getName(self
.type, self
.location
)
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
)
667 return "\t%sattribute %s %s\n" % (self
.readonly
and 'readonly ' or '',
668 self
.type, self
.name
)
670 class Method(object):
676 def __init__(self
, type, name
, attlist
, paramlist
, location
, doccomments
, raises
):
679 self
.attlist
= attlist
680 self
.params
= paramlist
681 self
.location
= location
682 self
.doccomments
= doccomments
685 for name
, value
, aloc
in attlist
:
686 if name
== 'binaryname':
688 raise IDLError("binaryname attribute requires a value",
691 self
.binaryname
= value
694 if value
is not None:
695 raise IDLError("Unexpected attribute value", aloc
)
697 if name
== 'noscript':
699 elif name
== 'notxpcom':
702 raise IDLError("Unexpected attribute '%s'", aloc
)
704 self
.namemap
= NameMap()
708 def resolve(self
, iface
):
710 self
.realtype
= self
.iface
.idl
.getName(self
.type, self
.location
)
711 for p
in self
.params
:
714 def isScriptable(self
):
715 if not self
.iface
.attributes
.scriptable
: return False
716 return not (self
.noscript
or self
.notxpcom
)
719 return "\t%s %s(%s)\n" % (self
.type, self
.name
, ", ".join([p
.name
for p
in self
.params
]))
723 raises
= ' raises (%s)' % ','.join(self
.raises
)
727 return "%s%s %s (%s)%s;" % (attlistToIDL(self
.attlist
),
731 for p
in self
.params
]),
743 def __init__(self
, paramtype
, type, name
, attlist
, location
, realtype
=None):
744 self
.paramtype
= paramtype
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':
755 raise IDLError("'size_is' must specify a parameter", aloc
)
757 elif name
== 'iid_is':
759 raise IDLError("'iid_is' must specify a parameter", aloc
)
762 if value
is not None:
763 raise IDLError("Unexpected value for attribute '%s'" % name
,
768 elif name
== 'array':
770 elif name
== 'retval':
772 elif name
== 'shared':
774 elif name
== 'optional':
777 raise IDLError("Unexpected attribute '%s'" % name
, aloc
)
779 def resolve(self
, method
):
780 self
.realtype
= method
.iface
.idl
.getName(self
.type, self
.location
)
782 self
.realtype
= Array(self
.realtype
)
784 def nativeType(self
):
786 if self
.shared
: kwargs
['shared'] = True
787 if self
.const
: kwargs
['const'] = True
790 return self
.realtype
.nativeType(self
.paramtype
, **kwargs
)
792 raise IDLError(e
.message
, self
.location
)
794 raise IDLError("Unexpected parameter attribute", self
.location
)
797 return "%s%s %s %s" % (paramAttlistToIDL(self
.attlist
),
803 def __init__(self
, 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):
816 'interface': 'INTERFACE',
820 'attribute': 'ATTRIBUTE',
822 'readonly': 'READONLY',
839 tokens
.extend(keywords
.values())
842 ('nativeid', 'exclusive'),
845 hexchar
= r
'[a-fA-F0-9]'
848 t_HEXNUM
= r
'0x%s+' % hexchar
852 literals
= '"(){}[],;:=|+-*'
856 def t_multilinecomment(self
, t
):
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
):
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')
874 def t_LCDATA(self
, t
):
875 r
'(?s)%\{[ ]*C\+\+[ ]*\n(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
877 t
.value
= t
.lexer
.lexmatch
.group('cdata')
878 t
.lexer
.lineno
+= t
.value
.count('\n')
881 def t_INCLUDE(self
, t
):
882 r
'\#include[ \t]+"[^"\n]+"'
883 inc
, value
, end
= t
.value
.split('"')
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
):
896 t
.lexer
.lineno
+= len(t
.value
)
898 def t_nativeid_NATIVEID(self
, t
):
900 t
.lexer
.begin('INITIAL')
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
))
913 ('left', 'LSHIFT', 'RSHIFT'),
919 def p_idlfile(self
, p
):
920 """idlfile : productions"""
923 def p_productions_start(self
, p
):
927 def p_productions_cdata(self
, p
):
928 """productions : CDATA productions"""
930 p
[0].insert(0, CDATA(p
[1], self
.getLocation(p
, 1)))
932 def p_productions_include(self
, p
):
933 """productions : INCLUDE productions"""
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"""
944 def p_typedef(self
, p
):
945 """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
946 p
[0] = Typedef(type=p
[2],
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],
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
967 p
[0] = {'value': p
[1],
968 'location': self
.getLocation(p
, 1)}
970 def p_attributes(self
, p
):
971 """attributes : '[' attlist ']'
974 p
[0] = {'attlist': []}
976 p
[0] = {'attlist': p
[2],
977 'doccomments': p
.slice[1].doccomments
}
979 def p_attlist_start(self
, p
):
980 """attlist : attribute"""
983 def p_attlist_continue(self
, p
):
984 """attlist : attribute ',' attlist"""
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 ')'
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']
1004 if 'doccomments' in atts
:
1005 doccomments
.extend(atts
['doccomments'])
1006 doccomments
.extend(p
.slice[2].doccomments
)
1008 l
= lambda: self
.getLocation(p
, 2)
1011 # forward-declared interface... must not have attributes!
1012 if len(attlist
) != 0:
1013 raise IDLError("Forward-declared interface must not have attributes",
1016 if base
is not None:
1017 raise IDLError("Forward-declared interface must not have a base",
1019 p
[0] = Forward(name
=name
, location
=l(), doccomments
=doccomments
)
1021 p
[0] = Interface(name
=name
,
1026 doccomments
=doccomments
)
1028 def p_ifacebody(self
, p
):
1029 """ifacebody : '{' members '}'
1034 def p_ifacebase(self
, p
):
1035 """ifacebase : ':' IDENTIFIER
1040 def p_members_start(self
, p
):
1044 def p_members_continue(self
, p
):
1045 """members : member members"""
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"""
1066 def p_number_hex(self
, p
):
1067 """number : HEXNUM"""
1071 def p_number_identifier(self
, p
):
1072 """number : IDENTIFIER"""
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 ')'"""
1081 def p_number_neg(self
, p
):
1082 """number : '-' number %prec UMINUS"""
1084 p
[0] = lambda i
: - n(i
)
1086 def p_number_add(self
, p
):
1087 """number : number '+' number
1089 | number '*' number"""
1093 p
[0] = lambda i
: n1(i
) + n2(i
)
1095 p
[0] = lambda i
: n1(i
) - n2(i
)
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"""
1105 p
[0] = lambda i
: n1(i
) << n2(i
)
1107 p
[0] = lambda i
: n1(i
) >> n2(i
)
1109 def p_number_bitor(self
, p
):
1110 """number : number '|' number"""
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:
1122 doccomments
= p
.slice[3].doccomments
1124 p
[0] = Attribute(type=p
[4],
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']
1136 doccomments
= p
.slice[2].doccomments
1138 p
[0] = Method(type=p
[2],
1140 attlist
=p
[1]['attlist'],
1142 location
=self
.getLocation(p
, 1),
1143 doccomments
=doccomments
,
1146 def p_paramlist(self
, p
):
1147 """paramlist : param moreparams
1153 p
[0].insert(0, p
[1])
1155 def p_moreparams_start(self
, p
):
1159 def p_moreparams_continue(self
, p
):
1160 """moreparams : ',' param moreparams"""
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],
1169 attlist
=p
[1]['attlist'],
1170 location
=self
.getLocation(p
, 3))
1172 def p_paramtype(self
, p
):
1178 def p_optreadonly(self
, p
):
1179 """optreadonly : READONLY
1182 p
[0] = p
.slice[1].doccomments
1186 def p_raises(self
, p
):
1187 """raises : RAISES '(' idlist ')'
1194 def p_idlist(self
, p
):
1195 """idlist : IDENTIFIER"""
1198 def p_idlist_continue(self
, p
):
1199 """idlist : IDENTIFIER ',' idlist"""
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
,
1213 self
.parser
= yacc
.yacc(module
=self
,
1214 outputdir
=outputdir
,
1215 debugfile
='xpidl_debug',
1216 tabmodule
='xpidlyacc',
1219 def clearComments(self
):
1220 self
._doccomments
= []
1223 t
= self
.lexer
.token()
1224 if t
is not None and t
.type != 'CDATA':
1225 t
.doccomments
= self
._doccomments
1226 self
._doccomments
= []
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__':
1241 for f
in sys
.argv
[1:]:
1242 print "Parsing %s" % f
1243 p
.parse(open(f
).read(), filename
=f
)