lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


>>I'm attaching my toLuaFrontend perl script (for reals this time - last
time
I forgot to actually attach it). Let me know if you find it useful.

Well I may well attach mine in Python :-)

Access is set using comments: eg. // "public" or // "private" . You should
include "lua" in the first comment of any files wanting to be scanned eg. /*
"lua" */

Nick
# Generate a Lua interface for a given application
# We'll do this automatically as keeping the interface files up to date in
# a rapidly changing project could be a bind.
# - Nick -
"""
    $Log: /Tools/build/genpkg.py $
 * 
 * 9     14/12/00 15:23 Nick
 * include inheritance as toLua can do this ie. widgets need it!
 * 
 * 8     7/12/00 16:13 Nick
 * stopped repetition of single lines
 * 
 * 6     7/12/00 15:23 Nick
 * commands now "public" and "private" classes set default access using
 * these
 * 
 * 5     30/11/00 13:47 Nick
 * Uses "keyword" format. Big speed increase as we only scan files with
 * "lua" in the header comment.
"""

import string,sys,re,os

class Parser:

    # get comment - we are looking for "lua"
    re_lua = re.compile("""
        \s*?        # spaces?
        /\*         # /* start of comment block
        (.*?)       # comment contents
        \*/         # */ end of comment
        """,re.VERBOSE | re.DOTALL)

    # one line comments containing // "commands"
    re_line = re.compile("""
        \s*
        (.*?)       # anything
        //          # // line comment
        \s*         # spaces
        "(\w+)"     # word in brackets
        \s*?        # spaces?
        (.*)        # rest of line
        $           # end of the line
        """,re.VERBOSE)

    # block comments containing 1st word /* "command" ... */
    re_comment_cmd = re.compile("""
        /\*\s+      # /* 
        "(\w+)"     # "keyword"
        \s+         # space
        (.+?)       # rest of contents of comment
        (\*/)       # */
        """, re.VERBOSE | re.DOTALL)

    def __init__(self):
        self.includes = None
        self.luacode = []
        self.interface = []
        self.textsize,self.nbfiles = 0,0
        self.scannedsize,self.scannedfiles = 0,0

    def ScanDir(self,arg,dirname,names):
        # callback from walk to scan given directory and list of names
        print 'Directory:',dirname,':'
        size = 0
        for n in names:
            if n[-2:]=='.h' or n[-4:]=='.cpp':
                fname = os.path.join(dirname,n)
                print '%19s' % n,
                try:
                    f = open(fname,'rt')
                except IOError:
                    print 'Couldnt open',fname
                    sys.exit(-1)
                txt = f.read()
                self.textsize += len(txt)
                self.nbfiles += 1
                f.close()
                hdr = Parser.re_lua.match(txt)
                if hdr:
                    if hdr.group(1).find('"lua"')>0:
                        # we found "lua" in the file header comment
                        self.scannedfiles += 1
                        self.scannedsize += len(txt)
                        self.FindLineComments(txt)
                        self.FindCommentCommands(txt)
        print

    def FindLineComments(self,txt):
        # find //"command" comments
        bInClass,bClassPublic,bClassAccess = 0,0,0
        bPublic = 0
        
        for line in txt.split('\n'):
            bTempPublic = 0

            # removed any ..EXPORT declarations
            if line.find('EXPORT')>-1:
                wds = string.split(line)
                if wds[1][-6:]=='EXPORT':
                    del wds[1]
                    line = string.join(wds)

            # if we're in a class take not of access eg. public:
            if bInClass:
                # when we reach an access fn set appropriate access!
                if ':' in line:
                    state = line[:line.index(':')]
                    if state=='public':
                        # set access to default public access of the class
                        bClassPublic = bClassAccess
                    elif state in ('private','protected'):
                        bClassPublic = 0

            # look for: //"command"
            cmnt = Parser.re_line.search(line)
            if cmnt:
                # found one
                before,cmd,rest = cmnt.groups()
                before.lstrip()
                wds = string.split(line)

                if cmd in ('public','private'):
                    # publish
                    if wds[0]=='class':
                        bInClass = 1
                        # set default access
                        bClassPublic = 0
                        bClassAccess = (cmd=='public')
                        name = wds[1]
                        self.interface.append('%s\n{' % before)
                    else:
                        # not a class
                        if before!='':
                            # just one line with stuff before the comment
                            # strip off xEXPORT if it exists
                            if cmd=='public':
                                # this is a single line // "public" comment
                                bTempPublic = 1
                        else:
                            if bInClass:
                                bClassPublic = (cmd=='public')
                            else:
                                bPublic = (cmd=='public')
                else:
                    raise '\nUnrecognised command: %s\n%s' % (cmd,line)

            if bInClass:
                if len(line)>0 and line[0]=='}':
                    # we're at the end of a class def eg. };
                    bInClass = 0
                    self.interface.append('};\n\n')
                elif bTempPublic or bClassPublic:
                    self.interface.append(line)
            else:
                if bTempPublic or bPublic:
                    self.interface.append(line)

    def FindCommentCommands(self,txt):
        # find the /* "command" */ type comments
        p = 0
        while p<len(txt):
            inst = Parser.re_comment_cmd.search(txt,p)
            if not inst:
                break # no more
            p = inst.end(3)+1
            keyword,code,cmnt = inst.groups()
            if keyword=='pkg':
                self.interface.append(code)
            elif keyword=='lua':
                self.luacode.append(code)
            elif keyword=='includes':
                assert not self.includes,'Includes already set!'
                self.includes = code
            else:
                raise '\nUnrecognised keyword: %s' % keyword

    def Write(self,pkg,codefile=None):
        # Write out the pkg file
        try:
            f = open(pkg,'wt')
        except IOError:
            print 'Cannot open .pkg to write:',pkg
            sys.exit(-1)
        f.write('\n// This file was automatically generated by genpkg.py\n')        
        f.write('// Any editing of this file will be lost! (Nick)\n\n')
        f.write('// includes for wrapper code :-\n\n')
        if self.includes:
            f.write(self.includes)
        else:
            f.write('// No include files specified\n')
        if codefile:
            self.WriteCode(codefile)
        else:
            f.write('\n\n// Embedded Lua code :-\n\n')
            for i in self.luacode:
                f.write('$[\n%s\n$]' % i)
                f.write('\n')
        f.write('\n\n// C++ wrapper :-\n\n')
        for i in self.interface:
            f.write(i)
            f.write('\n')
        f.close()
        if self.includes:
            print 'Nb includes:',len(self.includes)
        print 'Nb Lua code items:',len(self.luacode)
        print 'Nb interface items:',len(self.interface)

        print 'Nb "lua" files scanned: %d of %d (%.1f%%)' % \
              (self.scannedfiles,self.nbfiles,
               100.0*self.scannedfiles/self.nbfiles)

        print 'Amount of text scanned: %d bytes of %d (%.1f%%)' % \
              (self.scannedsize,self.textsize,
               100.0*self.scannedsize/self.textsize)

    def WriteCode(self,fname):
        try:
            f = open(fname,'wt')
        except IOError:
            print 'Error: Could not open',fname
            sys.exit(-1)
        f.write('-- This Lua file was automatically generated with output\n')
        f.write('-- from gpkg. Dont bother editing it!\n\n')
        for i in self.luacode:
            f.write(i)
            f.write('\n')
        f.close()
        print 'Core written to:',fname
        
            
def Scan(pkg,dirs,codefile=None):
    if not pkg:
        print 'No .pkg (destination) file given.'
        sys.exit(-1)
    if not dirs:
        print 'No scan directories given'
        sys.exit(-1)
    pars = Parser()
    print '='*79
    for d in dirs:
        os.path.walk(d,pars.ScanDir,None)
        print '-'*79
    pars.Write(pkg,codefile)
    print 'Written:',pkg

def Usage():
    # Note this python script is called using the batch file gpkg.bat
    print 'gpkg [-code file] <destination.pkg> <source dir> [<source dir>...]'
    print '     -code sends any Lua code to a file'
    print '     eg. gpkg w:/blah/hat.pkg w:/up/down w:/hatstand'
    print
    sys.exit(0)

if __name__=='__main__':
    args = sys.argv[1:]
    if len(args)<2:
        print 'Error: No arguments\n'
        Usage()
    codefile=None
    if args[0]=='-code':
        codefile=args[1]
        args=args[2:]
    pkg = args[0]
    dirs = args[1:]
    Scan(pkg,dirs,codefile)