woot, nsIFile at least has the correct class declaration. Now for all that other macrology`
authorBenjamin Smedberg <benjamin@smedbergs.us>
Sat, 14 Jun 2008 23:27:01 -0400
changeset 7 c9cc546fc95a
parent 6 1914d690fadd
child 8 ea4b1c101668
push id6
push userbsmedberg@mozilla.com
push dateSun, 15 Jun 2008 03:27:17 +0000
woot, nsIFile at least has the correct class declaration. Now for all that other macrology`
header.py
xpidl.py
--- a/header.py
+++ b/header.py
@@ -2,16 +2,63 @@
 
 """Print a C++ header file for the IDL files specified on the command line"""
 
 import sys, os.path, re, xpidl
 
 def firstCap(str):
     return str[0].upper() + str[1:]
 
+def attributeAsNative(a, getter):
+        scriptable = a.isScriptable() and "NS_SCRIPTABLE " or ""
+        binaryname = a.binaryname is not None and a.binaryname or firstCap(a.name)
+        paramname = "a" + firstCap(a.name)
+
+        params = {'scriptable': scriptable,
+                  'binaryname': binaryname,
+                  'paramname': paramname,
+                  'type': a.realtype.nativeType(getter and 'out' or 'in'),
+                  'get': getter and 'Get' or 'Set'}
+        return "%(scriptable)sNS_IMETHOD %(get)s%(binaryname)s(%(type)s%(paramname)s)" % params
+
+def methodAsNative(m):
+    scriptable = m.isScriptable() and "NS_SCRIPTABLE " or ""
+    binaryname = m.binaryname is not None and m.binaryname or firstCap(m.name)
+
+    return "%sNS_IMETHOD %s(%s)" % (scriptable,
+                                    binaryname,
+                                    paramlistAsNative(m.params, m.realtype))
+
+def paramlistAsNative(l, rettype):
+    l = list(l)
+    if rettype.name != 'void':
+        l.append(xpidl.Param(paramtype='out',
+                             type=None,
+                             name='_retval',
+                             attlist=[],
+                             location=None,
+                             realtype=rettype))
+
+    if len(l) == 0:
+        return 'void'
+
+    return ", ".join([paramAsNative(p) for p in l])
+
+def paramAsNative(p):
+    if p.paramtype == 'in':
+        typeannotate = ''
+    else:
+        typeannotate = ' NS_%sPARAM' % p.paramtype.upper()
+
+    return "%s%s%s" % (p.realtype.nativeType(p.paramtype),
+                       p.name,
+                       typeannotate)
+
+    
+
 header = """/*
  * DO NOT EDIT.  THIS FILE IS GENERATED FROM %(filename)s
  */
 
 #ifndef __gen_%(basename)s_h__
 #define __gen_%(basename)s_h__
 """
 
@@ -95,43 +142,44 @@ iface_epilog = """};
 
 """
 
 def write_interface(iface, fd):
     if iface.namemap is None:
         raise Exception("Interface was not resolved.")
 
     def write_const(c):
+        for comment in c.doccomments:
+            fd.write("  %s\n" % comment)
+
         basetype = c.basetype
         value = c.getValue(iface)
 
         fd.write("  enum { %(name)s = %(value)s%(signed)s };\n\n" % {
                      'name': c.name,
                      'value': value,
                      'signed': (not basetype.signed) and 'U' or ''})
 
     def write_method(c):
-        pass
-
-    def write_attr(c):
-        fd.write("  /* %(ro)sattribute %(type)s %(name)s; */\n" % {
-                     'ro': c.readonly and "readonly " or "",
-                     'type': c.type,
-                     'name': c.name});
+        for comment in c.doccomments:
+            fd.write("  %s\n" % comment)
 
-        scriptable = c.isScriptable() and "NS_SCRIPTABLE " or ""
-        binaryname = c.binaryname is not None and c.binaryname or firstCap(c.name)
-        paramname = "a" + firstCap(c.name)
-        realintype = c.realtype.nativename
+        fd.write("  /* %s */\n" % c.toIDL())
+        fd.write("  %s = 0;\n\n" % methodAsNative(c))
+                                                                           
+    def write_attr(c):
+        for comment in c.doccomments:
+            fd.write("  %s\n" % comment)
 
-        fd.write("  %(scriptable)sNS_IMETHOD Get%(binaryname)s(%(realintype)s %(paramname)s);\n" % {
-                     'scriptable': scriptable,
-                     'binaryname': binaryname,
-                     'realintype': realintype,
-                     'paramname': paramname})
+        fd.write("  /* %s */\n" % c.toIDL());
+
+        fd.write("  %s = 0;\n" % attributeAsNative(c, True))
+        if not c.readonly:
+            fd.write("  %s = 0;\n" % attributeAsNative(c, False))
+        fd.write("\n")
 
     defname = iface.name.upper()
     if iface.name[0:2] == 'ns':
         defname = 'NS_' + defname[2:]
 
     names = uuid_decoder.match(iface.attributes.uuid).groupdict()
     m3str = names['m3'] + names['m4']
     names['m3joined'] = ", ".join(["0x%s" % m3str[i:i+2] for i in xrange(0, 16, 2)])
--- a/xpidl.py
+++ b/xpidl.py
@@ -1,30 +1,61 @@
 #!/usr/bin/env python
 
 """A parser for cross-platform IDL (XPIDL) files."""
 
 import lex, yacc, sys, os.path
 
+"""A type conforms to the following pattern:
+
+    def isScriptable(self):
+        'returns True or False'
+
+    def nativeType(self, calltype):
+        'returns a string representation of the native type
+        calltype must be 'in', 'out', or 'inout'
+
+Interface members const/method/attribute conform to the following pattern:
+
+    name = 'string'
+
+    def toIDL(self):
+        'returns the member signature as IDL'
+"""
+
+def attlistToIDL(attlist):
+    if len(attlist) == 0:
+        return ''
+
+    return '[%s] ' % ','.join(["%s%s" % (name, value is not None and '(%s)' % value or '')
+                              for name, value, aloc in attlist])
+
 class BuiltinLocation(object):
     def get(self):
         return "<builtin type>"
 
     def __str__(self):
         return self.get()
 
 class Builtin(object):
     location = BuiltinLocation
 
     def __init__(self, name, nativename, signed=False, maybeConst=False):
         self.name = name
         self.nativename = nativename
         self.signed = signed
         self.maybeConst = maybeConst
 
+    def isScriptable(self):
+        return True
+
+    def nativeType(self, calltype):
+        return "%s %s" % (self.nativename,
+                          calltype != 'in' and '*' or '')
+
 builtinNames = [
     Builtin('boolean', 'PRBool'),
     Builtin('void', 'void'),
     Builtin('octet', 'unsigned char'),
     Builtin('short', 'PRInt16', True, True),
     Builtin('long', 'PRInt32', True, True),
     Builtin('long long', 'PRInt64', True, False),
     Builtin('unsigned short', 'PRUint16', False, True),
@@ -185,30 +216,44 @@ class Typedef(object):
         self.name = name
         self.location = location
         self.nativename = name
 
     def resolve(self, parent):
         parent.setName(self)
         self.realtype = parent.getName(self.type, self.location)
 
+    def isScriptable(self):
+        return self.realtype.isScriptable()
+
+    def nativeType(self, calltype):
+        return "%s %s" % (self.name,
+                          calltype != 'in' and '*' or '')
+
     def __str__(self):
         return "typedef %s %s\n" % (self.type, self.name)
 
 class Forward(object):
     kind = 'forward'
 
     def __init__(self, name, location):
         self.name = name
         self.location = location
         self.nativename = name
 
     def resolve(self, parent):
         parent.setName(self)
 
+    def isScriptable(self):
+        return True
+
+    def nativeType(self, calltype):
+        return "%s %s" % (self.name,
+                          calltype != 'in' and '* *' or '*')
+
     def __str__(self):
         return "forward-declared %s\n" % self.name
 
 class Native(object):
     kind = 'native'
 
     modifier = None
     specialtype = None
@@ -238,19 +283,43 @@ class Native(object):
                 if self.specialtype is not None:
                     raise IDLError("More than one special type", aloc)
                 self.specialtype = name
                 self.nativename = self.specialtypes[name]
                 self.scriptable = True
             else:
                 raise IDLError("Unexpected attribute", aloc)
 
+        if self.specialtype != 'nsid' and self.modifier is None:
+            raise IDLError("Native string type is neither 'ref' nor 'ptr'",
+                           location)
+
     def resolve(self, parent):
         parent.setName(self)
 
+    def isScriptable(self):
+        if self.specialtype is None:
+            return False
+
+        if self.specialtype == 'nsid':
+            return self.modifier is not None
+
+        return self.modifier == 'ref'
+
+    def nativeType(self, calltype):
+        const = ''
+        if self.modifier == 'ptr':
+            m = '*'
+        elif self.modifier == 'ref':
+            m = '& '
+            if calltype == 'in': const = 'const '
+        else:
+            m = ''
+        return "%s%s %s" % (const, self.nativename, m)
+
     def __str__(self):
         return "native %s(%s)\n" % (self.name, self.nativename)
 
 class Interface(object):
     kind = 'interface'
 
     def __init__(self, name, attlist, base, members, location, doccomments):
         self.name = name
@@ -279,16 +348,26 @@ class Interface(object):
                 raise IDLError("interface '%s' inherits from non-interface type '%s'" % (self.name, self.base), self.location)
 
             if self.attributes.scriptable and not realbase.attributes.scriptable:
                 raise IDLError("interface '%s' is scriptable but derives from non-scriptable '%s'" % (self.name, self.base))
 
         for member in self.members:
             member.resolve(self)
 
+    def isScriptable(self):
+        # NOTE: this is not whether *this* interface is scriptable... it's
+        # whether, when used as a type, it's scriptable, which is true of all
+        # interfaces.
+        return True
+
+    def nativeType(self, calltype):
+        return "%s %s" % (self.name,
+                          calltype != 'in' and '* *' or '*')
+
     def __str__(self):
         l = ["interface %s\n" % self.name]
         if self.base is not None:
             l.append("\tbase %s\n" % self.base)
         l.append(str(self.attributes))
         if self.members is None:
             l.append("\tincomplete type\n")
         else:
@@ -361,21 +440,22 @@ class InterfaceAttributes(object):
         if self.scriptable:
             l.append("\tscriptable\n")
         if self.function:
             l.append("\tfunction\n")
         return "".join(l)
 
 class ConstMember(object):
     kind = 'const'
-    def __init__(self, type, name, value, location):
+    def __init__(self, type, name, value, location, doccomments):
         self.type = type
         self.name = name
         self.value = value
         self.location = location
+        self.doccomments = doccomments
 
     def resolve(self, parent):
         self.realtype = parent.idl.getName(self.type, self.location)
         basetype = self.realtype
         while isinstance(basetype, Typedef):
             basetype = basetype.realtype
         if not isinstance(basetype, Builtin) or not basetype.maybeConst:
             raise IDLError("const may only be a short or long type, not %s" % self.type, self.location)
@@ -385,22 +465,24 @@ class ConstMember(object):
     def getValue(self, iface):
         return self.value(iface)
 
 class Attribute(object):
     kind = 'attribute'
     noscript = False
     notxpcom = False
 
-    def __init__(self, type, name, attlist, readonly, location):
+    def __init__(self, type, name, attlist, readonly, location, doccomments):
         self.type = type
         self.name = name
+        self.attlist = attlist
         self.readonly = readonly
         self.location = location
         self.binaryname = None
+        self.doccomments = doccomments
 
         for name, value, aloc in attlist:
             if name == 'binaryname':
                 if value is None:
                     raise IDLError("binaryname attribute requires a value",
                                    aloc)
 
                 self.binaryname = value
@@ -415,35 +497,42 @@ class Attribute(object):
                 self.notxpcom = True
             else:
                 raise IDLError("Unexpected attribute '%s'", aloc)
 
     def resolve(self, iface):
         self.iface = iface
         self.realtype = iface.idl.getName(self.type, self.location)
 
+    def toIDL(self):
+        attribs = attlistToIDL(self.attlist)
+        readonly = self.readonly and 'readonly ' or ''
+        return "%s%sattribute %s %s;" % (attribs, readonly, self.type, self.name)
+        
     def isScriptable(self):
         if not self.iface.attributes.scriptable: return False
         return not (self.noscript or self.notxpcom)
 
     def __str__(self):
         return "\t%sattribute %s %s\n" % (self.readonly and 'readonly ' or '',
                                           self.type, self.name)
 
 class Method(object):
     kind = 'method'
     noscript = False
     notxpcom = False
     binaryname = None
 
-    def __init__(self, type, name, attlist, paramlist, location):
+    def __init__(self, type, name, attlist, paramlist, location, doccomments):
         self.type = type
         self.name = name
+        self.attlist = attlist
         self.params = paramlist
         self.location = location
+        self.doccomments = doccomments
 
         for name, value, aloc in attlist:
             if name == 'binaryname':
                 if value is None:
                     raise IDLError("binaryname attribute requires a value",
                                    aloc)
 
                 self.binaryname = value
@@ -457,42 +546,53 @@ class Method(object):
             elif name == 'notxpcom':
                 self.notxpcom = True
             else:
                 raise IDLError("Unexpected attribute '%s'", aloc)
 
         self.namemap = NameMap()
         for p in paramlist:
             self.namemap.set(p)
-        #for p in paramlist:
-        #    p.validate(self)
 
     def resolve(self, iface):
         self.iface = iface
         self.realtype = self.iface.idl.getName(self.type, self.location)
         for p in self.params:
             p.resolve(self)
 
+    def isScriptable(self):
+        if not self.iface.attributes.scriptable: return False
+        return not (self.noscript or self.notxpcom)
+
     def __str__(self):
         return "\t%s %s(%s)\n" % (self.type, self.name, ", ".join([p.name for p in self.params]))
 
+    def toIDL(self):
+        return "%s%s %s (%s);" % (attlistToIDL(self.attlist),
+                                  self.type,
+                                  self.name,
+                                  ", ".join([p.toIDL()
+                                             for p in self.params]))
+
 class Param(object):
     size_is = None
     iid_is = None
     const = False
     array = False
     retval = False
     shared = False
     optional = False
 
-    def __init__(self, paramtype, type, name, attlist, location):
+    def __init__(self, paramtype, type, name, attlist, location, realtype=None):
         self.paramtype = paramtype
         self.type = type
         self.name = name
+        self.attlist = attlist
         self.location = location
+        self.realtype = realtype
 
         for name, value, aloc in attlist:
             # Put the value-taking attributes first!
             if name == 'size_is':
                 if value is None:
                     raise IDLError("'size_is' must specify a parameter", aloc)
                 self.size_is = value
             elif name == 'iid_is':
@@ -515,16 +615,22 @@ class Param(object):
                 elif name == 'optional':
                     self.optional = True
                 else:
                     raise IDLError("Unexpected attribute '%s'" % name, aloc)
 
     def resolve(self, method):
         self.realtype = method.iface.idl.getName(self.type, self.location)
 
+    def toIDL(self):
+        return "%s%s %s %s" % (attlistToIDL(self.attlist),
+                               self.paramtype,
+                               self.type,
+                               self.name)
+
 class IDLParser(object):
     keywords = {
         'const': 'CONST',
         'interface': 'INTERFACE',
         'in': 'IN',
         'inout': 'INOUT',
         'out': 'OUT',
         'attribute': 'ATTRIBUTE',
@@ -538,35 +644,33 @@ class IDLParser(object):
         'IDENTIFIER',
         'CDATA',
         'INCLUDE',
         'IID',
         'NUMBER',
         'HEXNUM',
         'LSHIFT',
         'RSHIFT',
-        'LBRACKET',
         'NATIVEID',
         ]
 
     tokens.extend(keywords.values())
 
     states = (
         ('nativeid', 'exclusive'),
     )
 
     hexchar = r'[a-fA-F0-9]'
 
     t_NUMBER = r'-?\d+'
     t_HEXNUM = r'0x%s+' % hexchar
     t_LSHIFT = r'<<'
     t_RSHIFT=  r'>>'
-    t_LBRACKET = r'\['
 
-    literals = '"(){}],;:=|+-*'
+    literals = '"(){}[],;:=|+-*'
 
     t_ignore = ' \t'
 
     def t_multilinecomment(self, t):
         r'/\*(?s).*?\*/'
         t.lexer.lineno += t.value.count('\n')
         if t.value.startswith("/**"):
             self._doccomments.append(t.value)
@@ -583,24 +687,22 @@ class IDLParser(object):
         t.type = self.keywords.get(t.value, 'IDENTIFIER')
         return t
 
     def t_LCDATA(self, t):
         r'%\{[ ]*C\+\+\s*\n(?s)(?P<cdata>.*?\n?)%\}[ ]*(C\+\+)?'
         t.type = 'CDATA'
         t.value = t.lexer.lexmatch.group('cdata')
         t.lexer.lineno += t.value.count('\n')
-        self.clearComments()
         return t
 
     def t_INCLUDE(self, t):
         r'\#include[ \t]+"[^"\n]+"'
         inc, value, end = t.value.split('"')
         t.value = value
-        self.clearComments()
         return t
 
     def t_newline(self, t):
         r'\n+'
         t.lexer.lineno += len(t.value)
 
     def t_nativeid_NATIVEID(self, t):
         r'[^()\n]+(?=\))'
@@ -647,40 +749,38 @@ class IDLParser(object):
         p[0] = list(p[2])
         p[0].insert(0, p[1])
 
     def p_typedef(self, p):
         """typedef : TYPEDEF IDENTIFIER IDENTIFIER ';'"""
         p[0] = Typedef(type=p[2],
                        name=p[3],
                        location=self.getLocation(p, 1))
-        self.clearComments()
 
     def p_native(self, p):
         """native : attributes NATIVE IDENTIFIER afternativeid '(' NATIVEID ')' ';'"""
         p[0] = Native(name=p[3],
                       nativename=p[6],
                       attlist=p[1]['attlist'],
                       location=self.getLocation(p, 2))
-        self.clearComments()
 
     def p_afternativeid(self, p):
         """afternativeid : """
         # this is a place marker: we switch the lexer into literal identifier
         # mode here, to slurp up everything until the closeparen
         self.lexer.begin('nativeid')
 
     def p_anyident(self, p):
         """anyident : IDENTIFIER
                     | CONST"""
         p[0] = {'value': p[1],
                 'location': self.getLocation(p, 1)}
 
     def p_attributes(self, p):
-        """attributes : LBRACKET attlist ']'
+        """attributes : '[' attlist ']'
                       | """
         if len(p) == 1:
             p[0] = {'attlist': []}
         else:
             p[0] = {'attlist': p[2],
                     'doccomments': p.slice[1].doccomments}
 
     def p_attlist_start(self, p):
@@ -727,18 +827,16 @@ class IDLParser(object):
         else:
             p[0] = Interface(name=name,
                              attlist=attlist,
                              base=base,
                              members=body,
                              location=l(),
                              doccomments=doccomments)
 
-        self.clearComments()
-
     def p_ifacebody(self, p):
         """ifacebody : '{' members '}'
                      | """
         if len(p) > 1:
             p[0] = p[2]
 
     def p_ifacebase(self, p):
         """ifacebase : ':' IDENTIFIER
@@ -757,17 +855,18 @@ class IDLParser(object):
 
     def p_member_cdata(self, p):
         """member : CDATA"""
         p[0] = CDATA(p[1], self.getLocation(p, 1))
 
     def p_member_const(self, p):
         """member : CONST IDENTIFIER IDENTIFIER '=' number ';' """
         p[0] = ConstMember(type=p[2], name=p[3],
-                           value=p[5], location=self.getLocation(p, 1))
+                           value=p[5], location=self.getLocation(p, 1),
+                           doccomments=p.slice[1].doccomments)
 
 # All "number" products return a function(interface)
 
     def p_number_decimal(self, p):
         """number : NUMBER"""
         n = int(p[1])
         p[0] = lambda i: n
 
@@ -816,30 +915,44 @@ class IDLParser(object):
     def p_number_or(self, p):
         """number : number '|' number"""
         n1 = p[1]
         n2 = p[3]
         p[0] = lambda i: n1(i) | n2(i)
 
     def p_member_att(self, p):
         """member : attributes optreadonly ATTRIBUTE IDENTIFIER IDENTIFIER ';'"""
+        if 'doccomments' in p[1]:
+            doccomments = p[1]['doccomments']
+        elif p[2] is not None:
+            doccomments = p[2]
+        else:
+            doccomments = p.slice[3].doccomments
+
         p[0] = Attribute(type=p[4],
                          name=p[5],
                          attlist=p[1]['attlist'],
-                         readonly=p[2],
-                         location=self.getLocation(p, 1))
+                         readonly=p[2] is not None,
+                         location=self.getLocation(p, 1),
+                         doccomments=doccomments)
 
     def p_member_method(self, p):
         """member : attributes IDENTIFIER IDENTIFIER '(' paramlist ')' raises ';'"""
+        if 'doccomments' in p[1]:
+            doccomments = p[1]['doccomments']
+        else:
+            doccomments = p.slice[2].doccomments
+
         # Ignoring "raises" for the moment, because I think it's unused
         p[0] = Method(type=p[2],
                       name=p[3],
                       attlist=p[1]['attlist'],
                       paramlist=p[5],
-                      location=self.getLocation(p, 1))
+                      location=self.getLocation(p, 1),
+                      doccomments=doccomments)
 
     def p_paramlist(self, p):
         """paramlist : param moreparams
                      | """
         if len(p) == 1:
             p[0] = []
         else:
             p[0] = list(p[2])
@@ -866,17 +979,20 @@ class IDLParser(object):
         """paramtype : IN
                      | INOUT
                      | OUT"""
         p[0] = p[1]
 
     def p_optreadonly(self, p):
         """optreadonly : READONLY
                        | """
-        p[0] = len(p) > 1
+        if len(p) > 1:
+            p[0] = p.slice[1].doccomments
+        else:
+            p[0] = None
 
     def p_raises(self, p):
         """raises : RAISES '(' idlist ')'
                   | """
 
     def p_idlist(self, p):
         """idlist : IDENTIFIER"""
 
@@ -893,17 +1009,18 @@ class IDLParser(object):
         self.parser = yacc.yacc(module=self)
 
     def clearComments(self):
         self._doccomments = []
 
     def token(self):
         t = self.lexer.token()
         if t is not None:
-            t.doccomments = list(self._doccomments)
+            t.doccomments = self._doccomments
+            self._doccomments = []
         return t
 
     def parse(self, data, filename=None):
         if filename is not None:
             self.lexer.filename = filename
         self.lexer.lineno = 1
         self.lexer.input(data)
         return self.parser.parse(lexer=self)