Previous Next Table of Contents

13. Tools for supporting both environments

13.1 XBPP(Y).CH

The following is a header file that will help Xbase++ compile Class(y) oriented code. The header file isn't perfect and there is no doubt that I've missed out some of the more obscure (and possibly some of the less obscure) problems. However, the following should get you started.

#ifndef __XBPPY_CH

   #define __XBPPY_CH

   #ifdef __XPP__

      // Class definition syntax

      #xcommand CREATE CLASS <x>                             ;
                =>                                           ;
                CLASS <x>
      #xcommand CREATE CLASS <x> <inh:FROM,INHERIT> <y>      ;
                =>                                           ;
                #xtranslate :super => :<y>                  ;;
                CLASS <x> FROM <y>
      #xcommand END CLASS [<*x*>]                            ;
                =>                                           ;
                ENDCLASS
      #xcommand MESSAGE <x> METHOD <y>                       ;
                =>                                           ;
                METHOD <x> IS <y>
      #xcommand MESSAGE <x> IS DEFERRED                      ;
                =>                                           ;
                METHOD <x> IS DEFERRED
      #xcommand CLASS MESSAGE <x> METHOD <y>                 ;
                =>                                           ;
                CLASS METHOD <x> IS <y>
      #xcommand VISIBLE:                                     ;
                =>                                           ;
                EXPORT:
      #xcommand VAR <x> [<ro:READONLY>] TYPE <y>             ;
                =>                                           ;
                VAR <x> <ro>
      #xcommand CLASS VAR <x> [<ro:READONLY>] TYPE <y>       ;
                =>                                           ;
                CLASS VAR <x> <ro>
      #xcommand METHOD <x>( [<y,...>] ), ( [<z,...>] )       ;
                =>                                           ;
                METHOD <x>( <y> )                           ;;
                Local ___csy_hack___ := ::super:<x>( <z> )
      #xcommand CLASS METHOD <x>( [<y,...>] ), ( [<z,...>] ) ;
                =>                                           ;
                CLASS METHOD <x>( <y> )                     ;;
                Local ___csy_hack___ := ::super:<x>( <z> )

      // This one is for debugging/research only.

//    #xcommand METHOD <x> <ctor:CTOR,CONSTRUCTOR>           ;
//              =>                                           ;
//              METHOD <x>

      // Or, you chould chose to use:

      #xcommand METHOD <x> <ctor:CTOR,CONSTRUCTOR>           ;
                =>                                           ;
                #error "Xbase++ doesn't support alternative constructors"

      // Hacks to get some Class(y) features into Xbase++

      #xtranslate :class           => :classObject()
      #xtranslate :isKindOf( <o> ) => :isDerivedFrom( <o> )
      #xtranslate @:               => ::

   #else

      #include "class(y).ch"

   #endif

#endif

You should include the above header file instead of including CLASS(Y).CH, the header file detects if you are compiling under Xbase++ and if you are not it will include CLASS(Y).CH for you.

13.2 CSY2XBPP.PY

The following is a simple Python script that will read in a set of Clipper PRG files and write out a set of corresponding PRX files. The PRX files (which you'll probably want to rename back to PRG) will have had a couple of changes made to the source. They are:

CLASS(Y).CH

Every instance of #include "class(y).ch" will be re-written as #include "CSY2XBPP.CH".

CLASS METHOD

Every class method implementation will be recoded as:

#ifdef __XPP__
Class Method initClass()
#else
Method initClass()
#endif

The code for the script is as follows:

#!/usr/local/bin/python

import re
import sys
import glob
import string

class ClassyToXbasePP :
    "Take Clipper/Class(y) source code and make it Xbase++ friendly"

    def __init__( self, fileSpec = glob.glob( "*.prg" ) ) :
        "Class Constructor"
        self.fileSpec    = fileSpec
        self.newHeader   = "XBPP(Y).CH"
        self.indent      = "   "
        self.reInclude   = re.compile( r"^\s*#include\s+\"class\(y\)\.ch\"",\
                                       re.IGNORECASE )
        self.reClassMeth = re.compile( r"^\s*Class\s+Method\s+" +\
                                       "([^\s]+).*$",\
                                       re.IGNORECASE )
        self.reClassMsg  = re.compile( r"^\s*Class\s+Message\s+" +\
                                       "[^\s]+\s+Method\s+" +\
                                       "([^\s]+).*$",\
                                       re.IGNORECASE )
        self.reClassImp  = re.compile( r"^\s*Method\s+" +\
                                       "([^\s\(]+)(\(.*)$",\
                                       re.IGNORECASE )

    def convert( self ) :
        "Do the source code conversion"
        for file in self.fileSpec :
            hFile = open( file, "r" )
            print file + " => " + self.targetName( file )
            if hFile :
                hTarget = self.openTarget( file )
                if hTarget :
                    classMeths = {}
                    while 1 :
                        line = hFile.readline()
                        if not line :
                            break
                        self.checkForClassMethods( line, classMeths )
                        if self.handleInclude( line, hTarget ) :
                            continue
                        if self.handleClassMeth( line, classMeths, hTarget ) :
                            continue
                        hTarget.write( line );
                    hTarget.close()
                hFile.close()
            else :
                self.logMessage( "Error: Can't open " + file )

    def checkForClassMethods( self, line, classMeths ) :
        "Look out for class method definitions"
        match = self.reClassMeth.match( line )
        if match :
            classMeths[ string.upper( match.group( 1 ) ) ] = 1
            self.logMessage( "Found class method definition for '" +\
                             match.group( 1 ) + "'" )
        else :
            match = self.reClassMsg.match( line )
            if match :
                classMeths[ string.upper( match.group( 1 ) ) ] = 1
                self.logMessage( "Found class message definition for '" +\
                                 match.group( 1 ) + "'" )
    
    def handleInclude( self, line, out = sys.stdout ) :
        "Handle the include of the Class(y) header"
        match = self.reInclude.match( line )
        if match :
            out.write( "#include \"" + self.newHeader + "\"\n" )
            self.logMessage( "Replaced Class(y).Ch include" )
        return( match )

    def handleClassMeth( self, line, classMeths, out = sys.stdout ) :
        "Handle a class method implementation"
        iHandled = 0
        match = self.reClassImp.match( line )
        if match :
            if classMeths.has_key( string.upper( match.group( 1 ) ) ) :
                iHandled = 1
                out.write(
                    "#ifdef __XPP__\n" +\
                    "Class Method " + match.group( 1 ) +\
                    match.group( 2 ) + "\n" +\
                    "#else\n" +\
                    line +\
                    "#endif\n" )
                self.logMessage( "Fixed class method implementation for '" +\
                                 match.group( 1 ) + "'" )
        return( iHandled )
            
    def openTarget( self, file ) :
        "Create and open the target source file"
        return( open( self.targetName( file ), "w" ) )

    def targetName( self, file ) :
        "Create a target file name"
        return( file[ 0 : string.rfind( file, "." ) ] + ".prx" )

    def logMessage( self, msg ) :
        "Print a log message"
        print self.indent + msg

if __name__ == "__main__" :
    if sys.argv[ 1: ] :
        ClassyToXbasePP( sys.argv[ 1: ] ).convert()
    else :
        ClassyToXbasePP().convert()

In case you are wondering why I've chosen to write the above in Python and not (for example) Clipper. Well, I'm a programmer and I like to choose the right tools for the right job. The problem in this case calls out for a strong language that can handle regular expressions, so, something like perl or python is the perfect tool for solving this problem. If you've not tried tools like this before, do yourself a favour and go download one or both of them now. If you don't want to expand your knowledge of other languages, feel free to send me some Clipper code that solves the above problem, I'll be more than happy to include it.


Previous Next Table of Contents