From: garys at magna.com.au (Gary Stephenson)
Date: Tue, 06 Apr 1999 13:06:35 +1000
Subject: Xbase++ preprocessor implementation in Python
Message-ID: <37097A3A.F772205@magna.com.au>
Content-Length: 28569
X-UID: 200                                                  


Attached please find a Python implementation of the Xbase++
preprocessor.  It
is not yet a totally perfect rendition, but it is pretty close.  By
implication, it should also be a reasonable rendition of the Clipper
preprocessor (though I have not yet tested this).

Oh, and it is *dead slow*! - hundreds of times slower I think!  This is
undoubtedly partly due to me being a Python newbie, and I would greatly
appreciate any tips on how I might speed it up a bit.  Any tips on how
the code could be improved would be equally welcome, particularly any
Python idioms that I have obviously not yet groked. (Error handling for
example!!)

I could not figure out how to make either KJparsing or TRAP do what I
wanted,
and I haven't yet looked at John Aylcock's stuff (but I intend to!), so
the code is built from scratch around the re module.  Any comments as to
the relative (de)merits of this approach are welcomed.

I would like eventually to make this available to the Clipper/Xbase++
community. But I want to present Python in the best possible light - so
I thought it best to first release it here in the hope that it might be
further refined first.

"Why write such a program?" I hear you ask! Several reasons :

-  I needed a concrete project to help me get up to speed with Python.

-  I want to translate some or all of my Clipper/Xbase++ source to
Python, and I'm hoping this will help (but I'm not quite sure how!).

-  the Clipper/Xbase++ preprocessor had always been a bit of a mystery
to me, so I thought this might deepen my understanding of it a bit (it
has!).

-  Ditto for regular expressions!

-  I have taken the opportunity to make some "improvements".  Some
patterns that do not successfully translate under Xbase++, but IMHO
should, do under PRGPP.

-  There are some concerns in the Xbase++ community as to
incompatibilities between the Clipper and Xbase++ preprocessors.
Hopefully, this tool might serve to clarify and/or resolve such issues.

-  Clipper and Xbase++ are only available on Window$ :-(

-  I love programming, the open-source idea, Python and the
Clipper/Xbase++/VO
languages. I would like eventually to see an open-source implementation
of a
such a language. Nuff said.

many tias,

gary


-------------- next part --------------
"""
    PRGPP.py  - a Python re-implementation of the Xbase++ (Clipper) preprocessor

    Released to the public domain 2-Apr-1999,

    Provided as-is; use at your own risk; no warranty; no promises; enjoy!

    by Gary Stephenson (garys at magna.com.au)
       (who is currently underemployed <g>)

"""

import re
import sys
import glob
import string

Identifier    = re.compile(r"\s*([A-Za-z_]+[A-Za-z0-9_]*)")
NumLiteral    = re.compile(r"\s*([0-9]+(\.[0-9]+)?)")
StringLiteral = re.compile(r"\s*('.*?'|\".*?\")")
LogLiteral    = re.compile(r"\s*(\.f\.|\.t\.)",re.I )
Operator      = re.compile(r"\s*(\+\+|\+=|\+|--|-=|->|-|\*\*|\*=|\*|/=|/|,|!|\||%|\.not\.|\.or\.|\.and\.|@|:=|:|==|=|>=|>|<=|<)",re.IGNORECASE)
BlockHeader   = re.compile(r"\s*\{\s*\|.*\|" )
OpenBrackets  = re.compile(r"\s*(\[|\(|{)")
CloseBrackets = re.compile(r"\s*(\]|\)|})")
eol           = re.compile(r"\s*$")
comma         = re.compile(r"\s*,")

defStmt    = re.compile( r"#\s*define\s+(?P<name>\w+)(?P<aft>.*)?", re.IGNORECASE )
ifdef      = re.compile( r"#\s*ifdef\s+(?P<name>\w+)", re.IGNORECASE )
ifndef     = re.compile( r"#\s*ifndef\s+(?P<name>\w+)", re.IGNORECASE )
elsedef    = re.compile( r"#\s*else\s*", re.IGNORECASE )
endif      = re.compile( r"#\s*endif\s*", re.IGNORECASE )
include    = re.compile( r"#\s*include\s+(?P<filename>.+)",re.IGNORECASE )
undef      = re.compile( r"#\s*undef\s+(?P<name>\w+)", re.IGNORECASE )
cmd        = re.compile( r"#\s*command\s*(?P<srch>.+)=>(?P<repl>.*)", re.IGNORECASE )
xcmd       = re.compile( r"#\s*xcommand\s+(?P<srch>.+)=>(?P<repl>.*)", re.IGNORECASE )
trans      = re.compile( r"#\s*trans(late)?\s+(?P<srch>.+)=>(?P<repl>.*)", re.IGNORECASE )
xtrans     = re.compile( r"#\s*xtrans(late)?\s+(?P<srch>.+)=>(?P<repl>.*)", re.IGNORECASE )
pragma     = re.compile( r"#\s*pragma\s*.+", re.IGNORECASE )

matchLit = re.compile( r"\s*([^\s^\[^\]^<]+)" )
matchGrp = re.compile( r"\s*\[" )
endGrp   = re.compile( r"\s*\]" )
matchMrk = re.compile( r"\s*<(.+?)>" )
DumbMrk  = re.compile( r"\s*#<(.+)>" )
firstNonWhite = re.compile( r"(\s*)\S" )

defs = { "__XPP__":None }
lineno = 0
curfile = ""
curIndent = ""

def StripComment( ln="" ) :
    global curIndent
    n = string.find( ln, '//' )
    if n <> -1 :
        ln = ln[:n]
    m = firstNonWhite.match(ln)
    if m :
        curIndent = m.group(1)
    return string.strip( ln )

def splitMatch( str, lft, rght ) :
    "Parses the string on balancing delimiters"
    j = string.find( str, lft )
    if j == -1 :
        return ( str,"","" )
    bef = str[:j]
    i = j + 1
    l = len( str )
    n = 0
    while i < l :
        if str[i] == rght :
            if n == 0 :
                return ( bef, str[j+1:i], str[i+1:] )
            n = n - 1
        elif str[i] == lft :
            n = n + 1
        i = i + 1
    assert 0, "Unbalanced delimiters detected"

def pseudofunc( key, str, line ) :
    i = string.find( str, ")" )
    srch = string.split( str[1:i], "," )
    str = str[i+1:]

    i = string.find( line, key )
    bef = line[:i]
    line = line[i+2:]
    l = len( line )
    n = 0
    i = 0
    while n < l :
        c = line[n]
        if  c == "(" :
            i = i + 1
        elif c == ")" :
            if i == 0 :
                break
            i = i - 1
        n = n + 1
    aft = line[n+1:]
    subs = string.split( line[:n],"," )
    l = len( subs )
    assert l==len(srch), "Length mismatch"
    for i in xrange( 0,l ) :
        str = replace( str, string.strip( string.rstrip( srch[i] ) ) , string.strip( string.rstrip( subs[i] ) ) )

    return bef + str + aft

def writeln( hFile ) :
    hFile.write( curIndent+tline+"\n" )

def writeBlank() :
    if rootfile == curfile :
        hOut.write( "\n" )

def readln( hFile ) :
    global lineno
    while 1 :
        s = hFile.readline()
        lineno = lineno+1
        if not s :
            hFile.close()
            break
        s = StripComment( s )
        if not s :
            writeBlank()
            continue
        if s[0] == "*" :
            writeBlank()
            continue
        if s[:2] != "/*" :
            break
        writeBlank()
        while string.rstrip(s)[-2:] != "*/" :
            writeBlank()
            s = hFile.readline()
            lineno = lineno+1
            if not s :
                hFile.close()
                print "Error : Unclosed Multiline Comments"
                sys.exit(1)

    return s

def readNextLine( hFile ) :
    s = readln( hFile )
    while s[-1:] == ";" :
        s = s[:-1] + " " + readln( hFile )
        writeBlank()
    return s

def defOmit( hFile ) :
    while 1 :
        line = readNextLine( hFile )

        m = endif.match( line )
        if m :
            return

        m = elsedef.match( line )
        if m :
            return

        m = ifdef.match( line )
        if m and not defs.has_key( m.group("name") ) :
            defOmit( hFile )

        m = ifndef.match( line )
        if m and defs.has_key( m.group("name") ) :
            defOmit( hFile )


class MatchGroup:
    """ see docstring for MatchTree"""

    def __init__( self ) :
        global curpos
        self.trees = []
        while 1:
            self.trees.append( MatchTree() )
            m = matchGrp.match( tline, curpos )
            if not m :
                break
            curpos = m.end()

    def __repr__( self ) :
        return self.pprint( 0 )

    def pprint( self, ind ) :
        s = ""
        for t in self.trees :
            s = s + t.pprint( ind+1 )
        return s

    def match( self ) :
        while 1 :
            mtch = 0
            for t in self.trees :
                if t.match() :
                    mtch = 1
                    break
            if not mtch :
                break
        return 1

class MatchTree:
    """
    MatchTree -> ( Literal | Marker | MatchGroup )+

    MatchGroup -> ( MatchTree )+

    MatchTree stores the "syntax tree" for each clause
    MatchGroup is a grouping of clauses at the same "level" (i.e. contiguous in the declaration)

    tline is the (global) string we are matching to
    curpos is the (global) current index into tline, which the match methods increment on success!
    """

    def __init__( self ) :
        self.tokens = []
        self.parse()

    def __repr__( self ) :
        return self.pprint()

    def pprint( self, ind=0 ) :
        s = ""
        for t in self.tokens :
            s = s + t.pprint( ind )
        s = s + ("    "*ind)+"--------------\n"
        return s

    def match( self ) :
        global curpos
        i = curpos
        for m in self.tokens :
            if not m.match() :
                curpos = i
                return 0
        return 1

    def search( self ) :
        global curpos
        i = curpos
        strt = self.tokens[0].search()
        if strt != -1 :
            for t in self.tokens[1:] :
                if not t.match() :
                    curpos = i
                    return -1
        return strt

    def parse( self ) :
        global curpos
        l = len( tline )
        while curpos < l :
            m = matchGrp.match( tline, curpos )
            if m :
                curpos = m.end()
                self.tokens.append( MatchGroup() )
                state = "G"
                continue

            m = endGrp.match( tline, curpos )
            if m :
                curpos = m.end()
                return

            m = matchMrk.match( tline,curpos )
            if m:
                body = m.group( 1 )
                curpos = m.end()
                c = body[0]
                if c == "(" :
                    self.tokens.append( ExtendedMarker( body[1:-1] ) )
                elif c == "#" :   # single
                    self.tokens.append( SingleMarker( body[1:] ) )
                elif c == "*" :   # wild
                    self.tokens.append( WildMarker( body[1:-1] ) )
                elif string.strip( body )[-3:] == "..."  : # list
                    n = string.find( body, "," )
                    self.tokens.append( ListMarker( string.strip( body[:n] ) ) )
                elif string.find( body, ":" ) > 0 : # restricted
                    self.tokens.append( RestrictedMarker( body ) )
                else :            # regular
                    self.tokens.append( RegularMarker( body ) )
                state = "M"
                continue

            m = Identifier.match( tline,curpos )
            if m :
                self.tokens.append( KeyWord( m.group(1) ) )
                curpos = m.end()
                continue

            m = matchLit.match( tline,curpos )
            if m :
                self.tokens.append( Literal( m.group(1) ) )
                curpos = m.end()
                continue

            assert 0, "Error - Unable to parse : "+tline[curpos:]

def readExpression( xpos=0, expect="",commaOK = 0 ) :

    prevTok = "O"
    while xpos < len( tline ) :
        m = BlockHeader.match( tline,xpos )
        if m :
            xpos = readExpression( m.end(), "}" )
            prevTok = "B"
            continue

        m = OpenBrackets.match( tline, xpos )
        if m :
            c = m.group(1)
            if( prevTok != "O" and prevTok != "I" ) :
                return xpos
            if c == "[" :
                bal = "]"
            elif c == "{" :
                bal = "}"
            else :       # c == "("
                bal = ")"
            xpos = readExpression( m.end(), bal, 1 )
            prevTok = "X"
            continue

        m = CloseBrackets.match( tline, xpos )
        if m :
            if expect :
                if m.group(1) != expect :
                    assert 0, "Unbalanced delimiters"
                return m.end()
            return xpos

        if not commaOK :
            m = comma.match( tline,xpos )
            if m :
                return xpos

        m = Operator.match(tline, xpos)
        if m :
            prevTok = "O"
            xpos = m.end()
            continue

        m = NumLiteral.match(tline, xpos)
        if not m :
            m = StringLiteral.match(tline, xpos)
        if not m :
            m = LogLiteral.match(tline, xpos)
        if m :
            if prevTok != "O" :
               return xpos
            prevTok = "L"
            xpos = m.end()
            continue

        m = Identifier.match(tline, xpos)
        if m :
            if prevTok != "O" :
                return xpos
            prevTok = "I"
            xpos = m.end()
            continue

        print "Error : Unable to parse string : "+tline[xpos:]
        sys.exit(1)

    return xpos

def ParseExpression() :
    global curpos
    i = curpos
    curpos = readExpression( i )
    return tline[i:curpos]

class Literal :

    def __init__( self, s ) :
        #self.re = re.compile( r"\s*"+re.escape( s )+r"(\b|$)", re.I )
        self.re = re.compile( r"\s*"+re.escape( s ), re.I )
        assert s

    def __repr__( self ) :
        return self.pprint()

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Literal : "+self.re.pattern+"\n"

    def match( self ) :
        global curpos
        m = self.re.match( tline, curpos )
        if m :
            curpos = m.end()
            return 1
        return 0

    def search( self ) :
        global curpos
        m = self.re.search( tline, curpos )
        if m :
            curpos = m.end()
            return m.start()
        return -1

class KeyWord :

    def __init__( self, s ) :
        self.re = re.compile( r"\s*\b"+ s +r"(\b|$)", re.I )
        assert s

    def __repr__( self ) :
        return self.pprint()

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Keyword : "+self.re.pattern+"\n"

    def match( self ) :
        global curpos
        m = self.re.match( tline, curpos )
        if m :
            curpos = m.end()
            return 1
        return 0

    def search( self ) :
        global curpos
        m = self.re.search( tline, curpos )
        if m :
            curpos = m.end()
            return m.start()
        return -1

class MatchMarker :

    def __init__( self, name ) :
        self.name = name
        self.vals = []
        if currentCmd.markers.has_key( name ) :
            print "Error - marker name already present : "+name
            sys.exit(1)
        currentCmd.markers[name] = self

    def __repr__( self ) :
        return self.pprint()

    def pprint( self,ind=0 ) :
        assert 0, "Abstract method called"
        return ""

    def match( self ) :
        assert 0, "Abstract method called"
        return 0

    def getVal( self, i=0, f=None ) :
        l = len( self.vals )
        if i >= l :
            if l :
                val = self.vals[l-1]
            else :
                val  =""
        else :
            val = self.vals[i]
        if f :
            return apply( f, (val,) )
        return val

class RegularMarker( MatchMarker ) :

    def __init__( self, name ) :
        MatchMarker.__init__( self,name )

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Regular : "+self.name+"\n"

    def match( self ) :
        self.vals.append( ParseExpression() )
        return 1

class RestrictedMarker( MatchMarker ) :

    def __init__( self, body ) :
        n = string.find( body, ":" )
        name = string.strip( body[:n] )
        MatchMarker.__init__( self, name )
        self.vals = []
        mstr = string.replace( body[n+1:],",","|" )
        self.re = re.compile( "\s*("+string.replace( mstr, " ", "" )+")", re.I )

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Regular : "+self.name+", "+self.re.pattern+"\n"

    def match( self ) :
        global curpos
        m = self.re.match( tline, curpos )
        if m :
            self.vals.append( m.group( 1 ) )
            curpos = m.end()
            return 1
        return 0

    def search( self ) :
        global curpos
        m = self.re.search( tline, curpos )
        if m :
            self.vals.append( m.group( 1 ) )
            curpos = m.end()
            return m.start()
        return -1

class WildMarker( MatchMarker ) :

    def __init__( self, name ) :
        MatchMarker.__init__( self,name )

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Wild : "+self.name+"\n"

    def match( self ) :
        global curpos
        self.vals.append( tline[curpos:] )
        curpos = len( tline )
        return 1

class SingleMarker( MatchMarker ) :

    def __init__( self, name ) :
        MatchMarker.__init__( self,name )
        self.re = re.compile( r"\s*([^\s]*)" )

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Single : "+self.name+"\n"

    def match( self ) :
        global curpos
        m = self.re.match( tline, curpos )
        if m :
            self.vals.append( m.group(1) )
            curpos = m.end()
            return 1
        return 0

class ExtendedMarker( MatchMarker ) :

    def __init__( self, name ) :
        MatchMarker.__init__( self,name )
        self.reXpr = re.compile("\s*\(")

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "Extended : "+self.name+"\n"

    def match( self ) :
        global curpos
        m = self.reXpr.match( tline, curpos )
        if m :
            self.vals.append( ParseExpression() )
            return 1

        m = StringLiteral.match( tline, curpos )
        if not m :
            m = Identifier.match( tline, curpos )
        if m :
            self.vals.append( m.group(1) )
            curpos = m.end()
            return 1

        return 0

class ListMarker( MatchMarker ) :

    def __init__( self, name ) :
        MatchMarker.__init__( self,name )

    def pprint( self, ind=0 ) :
        return ("    ")*ind + "List : "+self.name+"\n"

    def match( self ) :
        global curpos
        val = []
        self.vals.append( val )
        while 1:
            xpos = curpos
            curpos = readExpression(curpos)
            val.append( tline[xpos:curpos] )
            m = comma.match( tline,curpos )
            if not m:
                break
            curpos = m.end()
        return 1

    def getVal( self, i=0, f=None ) :
        l = len( self.vals )
        if i >= l :
            if l :
                val = self.vals[l-1]
            else :
                val = [""]
        else :
            val = self.vals[i]
            if not val :
                val.append("")
        if f :
            val = map( f, val )
        return reduce( lambda x,y : x+","+y, val )

class ResultGroup :

    def __init__( self ) :
        global curpos
        self.markers = []
        self.prs = ""
        l = len( tline )
        while curpos < l  :

            m = matchGrp.match( tline,curpos )
            if m :
                self.prs = self.prs + tline[m.start():m.end()-1] + "%s"
                curpos = m.end()
                self.markers.append( (ResultGroup(),) )
                continue

            if endGrp.match( tline,curpos ) :
                return

            Dumb = 0
            m = matchMrk.match( tline,curpos )
            if not m :
                Dumb = 1
                m = DumbMrk.match( tline,curpos )
            if m :
                self.prs = self.prs + tline[curpos:m.start()]
                body = m.group(1)
                curpos = m.end()
                c = body[0]
                if Dumb :
                    mname = body
                    func =  stringify
                elif c == '"' :
                    assert body[-1] == '"', "Invalid match marker : "+body
                    mname = body[1:-1]
                    func = normstringify
                elif c == "(" :
                    assert body[-1] == ')', "Invalid match marker : "+body
                    mname = body[1:-1]
                    func = smartstringify
                elif c == "{" :
                    assert body[-1] == '}', "Invalid match marker : "+body
                    mname = body[1:-1]
                    func = blockify
                elif c == "." :
                    assert body[-1] == '.', "Invalid match marker : "+body
                    mname = body[1:-1]
                    func = logify
                else :            # regular
                    mname = body
                    func = lambda s : s

                if not currentCmd.markers.has_key( mname ) :
                    print "Error : match marker not found for "+mname+" at line :",lineno
                    print tline
                    print currentCmd.mtree
                    sys.exit(1)

                self.markers.append( (currentCmd.markers[mname],func) )
                self.prs = self.prs + "%s"
                continue

            m = matchLit.match( tline, curpos )
            if m :
                self.prs = self.prs + tline[m.start():m.end()]
                curpos = m.end()

    def expand( self, i = 0 ) :
        l = []
        for m in self.markers :
            if len(m) == 2 :
                l.append( m[0].getVal( i, m[1] ) )
            else :
                l.append( m[0].repexpand() )
        return self.prs % tuple( l )

    def repexpand( self ) :   # Note : this should not have any repeating group markers!
        maxlen = 0
        Result = ""
        for m in self.markers :
            if len( m[0].vals ) > maxlen :
                maxlen = len( m[0].vals )
        for i in range( 0, maxlen ) :
            Result = Result + self.expand( i )
        return Result

class Command :

    cmds = []

    def __init__( self, srch, repl ) :
        global currentCmd,tline,curpos
        self.markers = {}
        currentCmd = self
        curpos = 0
        tline =  string.strip( srch )
        self.mtree = MatchTree()
        tline = string.strip( repl )
        curpos = 0
        self.repl = ResultGroup()
        Command.cmds.insert(0,self)

    def transform( self ) :
        global tline
        if self.mtree.match() :
            tline = self.repl.expand()
            t = self.markers.items()
            for (n,m) in t :
                m.vals = []
            return 1
        return 0

class Translate( Command ) :

    trns = []

    def __init__( self, srch, repl ) :
        global currentCmd,tline,curpos
        self.markers = {}
        currentCmd = self
        tline = string.strip( srch )
        curpos = 0
        self.mtree = MatchTree()
        tline = repl
        curpos = 0
        self.repl = ResultGroup()
        Translate.trns.insert(0,self)

    def transform( self ) :
        global tline
        i = self.mtree.search()
        if i != -1 :
            tline = tline[:i]+ self.repl.expand()+ tline[curpos:]
            t = self.markers.items()
            for (n,m) in t :
                m.vals = []
            return 1
            return 1
        return 0


def stringify( s ) :
    if string.lstrip( s )[0] == '"' :
        return "'"+s+"'"
    return '"'+s+'"'

def normstringify( s ) :
    if s :
        return '"'+s+'"'
    return s

def smartstringify( s ) :
    if not s :
        return ""
    s = string.strip( s )
    if s[0] == "(" and s[-1:] == ")" :
        return s
    return '"'+s+'"'

def blockify( s ) :
    if s :
        return "{|| "+s+" }"
    return ""

def logify( s ) :
    if s :
        return ".T."
    return ".F."

def applydefs() :
    global defs,tline
    for (frm,to) in defs.items() :
        i = string.find( tline, frm )
        if i <> -1 :
            tline = string.replace( tline, frm, to )
            return 1
    return 0

def applytrans() :
    for c in Translate.trns :
        if c.transform() :
            return 1
    return 0

def applycmds() :
    for c in Command.cmds :
        if c.transform() :
            return 1
    return 0

def transform( line ) :
    global tline,curpos
    tline = line
    assert tline, "Empty string before transform"
    curpos = 0
    while 1 :
        if applydefs() :
            continue
        if applytrans() :
            continue
        if applycmds() :
            continue
        break

    assert tline, "Empty string after transform"
    writeln( hOut )

def PreProcess( cFile ) :
    global defs,hOut,lineno,curfile

    hFile = open( cFile, "r" )
    if not hFile :
        print "Error : unable to open "+cFile
        sys.exit(1)

    savno = lineno
    savfile = curfile
    curfile = cFile
    lineno = 0
    while not hFile.closed :
        line = readNextLine(hFile)
        if not line :
            continue
        m = defStmt.match( line )
        if m :
            nam = m.group("name")
            if defs.has_key( nam ) :
                print "Error : : "+nam+" already defined"
                sys.exit(1)
            if not m.group("aft") :
                defs[nam] = ""    # preprocessor const
            elif m.group("aft")[0] == "(" :  #pseudofunction
                defs[nam+"("] = m.group("aft")
            else :      # symbolic constant
                defs[nam] = string.strip( string.rstrip( m.group("aft") ) )
            continue

        m = ifdef.match( line )
        if m :
            if not defs.has_key( m.group("name") ) :
                defOmit( hFile )
            continue

        m = ifndef.match( line )
        if m :
            if defs.has_key( m.group("name") ) :
                defOmit( hFile )
            continue

        m = elsedef.match( line )
        if m :
            defOmit( hFile )
            continue

        m = endif.match( line )
        if m :
            continue

        m = include.match( line )
        if m :
            fname = string.split( m.group("filename"),'"' )[1]
            IncludeFile( fname )
            continue

        m = undef.match( line )
        if m :
            if defs.has_key( m.group("name") ) :
                del defs[ m.group("name") ]
            continue

        m = xcmd.match( line )
        if m :
            Command( m.group("srch"),m.group("repl") )
            continue

        m = cmd.match( line )
        if m :
            Command( m.group("srch"),m.group("repl") )
            continue

        m = xtrans.match( line )
        if m :
            Translate( m.group("srch"),m.group("repl") )
            continue

        m = trans.match( line )
        if m :
            Translate( m.group("srch"),m.group("repl") )
            continue

        m = pragma.match( line )
        if m :
            continue

        transform( line )

    hFile.close()
    lineno = savno
    curfile = savfile

def IncludeFile( fname ) :
    import os
    if not os.path.exists( fname ) :
        if os.environ.has_key("INCLUDE") :
            incpaths = string.split( os.environ["INCLUDE"], ";" )
            for p in incpaths :
                if os.path.exists( p + "\\" + fname ) :
                    hOut.write( '#line 1 "'+p+"\\"+fname+'"@"'+fname+'"\n' )
                    fname = p+"\\"+fname
                    break
            else :
                print "Error : unable to find : "+fname
                sys.exit( 1 )
        else :
            print "Error : unable to find : "+fname
            sys.exit( 1 )
    else :
        hOut.write( '#line 1 "'+fname+'"@"'+fname+'"\n' )

    PreProcess( fname )
    if curfile :
        hOut.write( "#line "+str(lineno)+' "'+curfile+'"\n' )

def PreProcessPrg( prg, pp=None,std=None ) :
    import os
    global hOut,curfile,rootfile

    if "." not in prg :
        prg = prg+".prg"

    if not pp :
        pp = os.path.splitext(prg)[0]+".ppp"

    hOut = open( pp, "w" )
    rootfile = prg
    print "Preprocessing",prg,"to",pp,"using",std
    if std :
        IncludeFile( std )
    hOut.write( '#line 1 "'+prg+'"\n' )
    curfile = prg
    PreProcess( prg )

    hOut.close()


if __name__ == "__main__" :
    import getopt
    optlist, args = getopt.getopt( sys.argv[1:], "Uu:" )
    if not args or len(args) > 2 :
        print "Syntax : PRGPP [-U | -u <std.ch replacement> ] <filename> [ <ppp-name> ] "
    else :
        if len(args) < 2 :
            args.append( None )
        if optlist :
            if optlist[0][0] == "-U" :
                PreProcessPrg( args[0], args[1]  )
            elif optlist[0][0] == "-u"  :
                PreProcessPrg( args[0], args[1], optlist[0][1] )
        else :
            PreProcessPrg( args[0],args[1],"std.ch" )


