1040 lines
28 KiB
Plaintext
1040 lines
28 KiB
Plaintext
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" )
|
|
|
|
|