740 lines
35 KiB
Python
Executable File
740 lines
35 KiB
Python
Executable File
#!/usr/bin/python
|
|
|
|
# pcfconf.py : A utility to convert mathetmatical formulas
|
|
# from one language to another. it currently only supports
|
|
# Microsoft PowerToy Calculator save files for reading,
|
|
# and Python for writing. But it can be easily extended
|
|
# to handle other file formats; see SOURCE DOC below
|
|
# for details.
|
|
|
|
# TODO: Set default data type (short, int, long, double, float)
|
|
# it currently defaults to int or float, depending on
|
|
# what's in the file
|
|
# Implement some sanity checking on function arguments, to
|
|
# make sure only arguments present in the arg list or the
|
|
# parent object's body, are referenced. Currently, there
|
|
# are no such checks.
|
|
|
|
################# SOURCE DOC ##############################################
|
|
############## (read here if you want to extend this program) #############
|
|
|
|
# The actual conversion is done by XXXXConverter classes that
|
|
# do the actual conversion. They are passed a generic list of the following
|
|
# form:
|
|
#
|
|
# [ [TYPE, NAME, VALUE] ]
|
|
#
|
|
# where TYPE is either "VAR" or "FUNC". If TYPE is "VAR", then the rest of
|
|
# the values are obvious (the name and value of the variable). If TYPE is
|
|
# "FUNC", then VALUE is actually another list of the form:
|
|
#
|
|
# [ARGS, CODE]
|
|
#
|
|
# where ARGS is yet another list containing the names of the function's arguments,
|
|
# and CODE is the original PowerToyCalc code for the function.
|
|
#
|
|
# To extend this program with a new filetype, scan down to the "PROCESSOR CLASSES"
|
|
# comment. At this point there is no programmatic reason to inherit your classes
|
|
# from GenericProcessor, but GenericProcessor is to be considered the base class
|
|
# for all new file processors; if GenericProcessor has a method, your class
|
|
# must have it as well. Use the PythonProcessor class as a guide to how the existing
|
|
# code works, if you need to. But as long as the converter winds up printing usable
|
|
# code to the output file, and errors to sys.__stderr__ (via the *err functions), then
|
|
# you're doing it right. Once you've done that, add it to the "PROCESSORS" dictionary
|
|
# in the global variables section. The format of the dictionary is obvious.
|
|
#
|
|
# To extend this program with a new target language, scan down to the "GENERATOR
|
|
# CLASSES" comment. The general process for processor classes applies here; if it's
|
|
# in the base class, it needs to be in your new subclass. Use the PythonConverter
|
|
# class as a guide to how to write your own converter, if necessary. The converter just
|
|
# needs to return usable output to the processor class, which prints it to whatever
|
|
# output file has been specified. Once you've done that, add it to the "CONVERTERS"
|
|
# dictionary in the global variables section. The format of the dictionary is obvious.
|
|
#
|
|
# Got it? easy.
|
|
|
|
import sys
|
|
import getopt
|
|
import math
|
|
|
|
# Functions that will be used throughout the module.
|
|
|
|
def syntaxerr (linenum, line, err):
|
|
sys.__stderr__.write("%d : %s : %s\n" % (linenum, err, line))
|
|
|
|
def generalerr (err):
|
|
sys.__stderr__.write("%s\n" % err)
|
|
|
|
def debugmsg (err):
|
|
global DEBUG
|
|
if ( DEBUG ) :
|
|
sys.__stderr__.write("DEBUG: %s\n" % err)
|
|
|
|
def cloneList (list1):
|
|
list2 = []
|
|
for item in list1:
|
|
list2.append(item)
|
|
return list2
|
|
|
|
################## CONVERTER CLASSES #######################
|
|
|
|
class GenericConverter:
|
|
def __init__ (self, langopts, contextName=""):
|
|
"""contextname is the name of the class, context, or namespace that should be
|
|
generated for the incoming data. langopts is a dictionary containing
|
|
the language specific options that the processor should follow."""
|
|
return
|
|
|
|
def convertFile (self, data):
|
|
"""data is the data to interpret to your native language"""
|
|
generalerr("convertFile is not implemented in GenericConverter")
|
|
return 1
|
|
|
|
def convertCalls (self, code):
|
|
"""For the given block of code, translate any calls to things like atan() and
|
|
acos() to functions that exist in your language's internal math library."""
|
|
global MATHSYMS
|
|
ourlist = cloneList(MATHSYMS)
|
|
ourlist.append(",")
|
|
ourlist.append(" ")
|
|
ourlist.append("\t")
|
|
# we do our best here to ensure we don't change someone's defined function that
|
|
# happens to have the name of one of ours inside of it. For example, if someone
|
|
# makes a function MYacos(), then a replace on "acos" with "math.acos" would result
|
|
# in the code being MYmath.acos(), which is guaranteed to do nothing but explode.
|
|
# So we try to make sure that we're only replacing exact matches, by checking that
|
|
# it follows any of the items in MATHSYMS, a comma(,), " ", or \t. The downside
|
|
# to this is that it winds up being pretty slow.
|
|
for call in self.__subs__.keys():
|
|
for symbol in ourlist :
|
|
toreplace = "%s%s" % (symbol, call)
|
|
replacer = "%s%s" % (symbol, self.__subs__[call])
|
|
if ( toreplace in code ):
|
|
debugmsg("Replacing %s with %s in code line %s" % (toreplace, replacer, code))
|
|
code = code.replace(toreplace, replacer)
|
|
return code
|
|
|
|
def supportedTranslations (self):
|
|
"""return a string that lists all of the translations your language's
|
|
converter supports"""
|
|
generalerr("supportedTranslations is not implemetned in GenericConverter")
|
|
|
|
class CPPConverter:
|
|
class CPPVariable:
|
|
def __init__ (self, name, initValue, baseType, namespace):
|
|
self.__name__ = name
|
|
self.__value__ = initValue
|
|
self.__vartype__ = baseType
|
|
self.__namespace__ = namespace
|
|
def __str__ (self, defonly = False):
|
|
retStr = ""
|
|
if ( isinstance(self.__value__, list) or isinstance(self.__value__, tuple) ):
|
|
if ( defonly ):
|
|
return "%s %s[%d];" % (self.__vartype__, self.__name__, len(self.__value__))
|
|
# if we're not doing definitions, then assignments only occur in the constructor
|
|
for i in range(0, len(self.__value__)-1):
|
|
retStr += "\tthis->%s[%d] = %s;\n" % (self.__name__, i, self.__value__[i])
|
|
else:
|
|
if ( defonly ):
|
|
return "%s %s;" % (self.__vartype__, self.__name__)
|
|
retStr = "\tthis->%s = %s;" % (self.__name__, self.__value__)
|
|
return retStr
|
|
class CPPFunction:
|
|
def __init__ (self, namespace, argtype, rettype, name, arguments, code):
|
|
self.__rettype__ = rettype
|
|
self.__argtype__ = argtype
|
|
self.__name__ = name
|
|
self.__namespace__ = namespace
|
|
self.__args__ = arguments
|
|
self.__code__ = code
|
|
def replaceVars (self, varlist):
|
|
"""Find any instance of a variable in our code that is not in our argument list,
|
|
and is a member variable of our parent class"""
|
|
codeCopy = self.__code__
|
|
ourlist = cloneList(MATHSYMS)
|
|
ourlist.append(",")
|
|
ourlist.append(" ")
|
|
for var in varlist :
|
|
if ( var.__name__ in self.__args__ ):
|
|
continue
|
|
for symbol in ourlist:
|
|
toreplace = "%s%s" % (symbol, var.__name__)
|
|
replacer = "this->%s" % (var.__name__)
|
|
debugmsg("Searching for \" %s\" in code block %s" % (toreplace, codeCopy))
|
|
if ( toreplace in codeCopy ):
|
|
debugmsg("Replacing \"%s\" with \"%s\" in %s" % (toreplace, replacer, codeCopy))
|
|
codeCopy = codeCopy.replace(toreplace, replacer)
|
|
self.__code__ = codeCopy
|
|
def __str__ (self, defonly=False):
|
|
if ( defonly ):
|
|
str = "%s %s (" % (self.__rettype__, self.__name__)
|
|
else:
|
|
str = "%s %s::%s (" % (self.__rettype__, self.__namespace__, self.__name__)
|
|
argcnt = 0
|
|
for item in self.__args__:
|
|
if ( argcnt > 0 ):
|
|
str += ", "
|
|
str += "%s %s" % (self.__argtype__, item)
|
|
argcnt += 1
|
|
str += ")"
|
|
if ( defonly ):
|
|
return str+";"
|
|
str += "\n{\n\treturn (%s);\n}\n" % (self.__code__)
|
|
return str
|
|
class CPPClass:
|
|
def __init__ (self, namespace, variables, functions, langopts):
|
|
self.__namespace__ = namespace
|
|
self.__vars__ = variables
|
|
self.__funcs__ = functions
|
|
self.__langopts__ = langopts
|
|
def toASCII (self):
|
|
hdrstr = ""
|
|
impstr = ""
|
|
# generate definition
|
|
hdrstr = "#ifndef __%s__\n" % self.__namespace__
|
|
hdrstr += "#define __%s__\n" % self.__namespace__
|
|
hdrstr += "#include <math.h>\n\n"
|
|
hdrstr += "class %s {\n" % self.__namespace__
|
|
hdrstr += "public:\n"
|
|
hdrstr += "\t%s::%s(void);\n" % (self.__namespace__, self.__namespace__)
|
|
for func in self.__funcs__:
|
|
hdrstr += "\t%s\n" % func.__str__(True)
|
|
hdrstr += "protected:\n"
|
|
for var in self.__vars__ :
|
|
hdrstr += "\t%s\n" % (var.__str__(True))
|
|
hdrstr += "}\n"
|
|
hdrstr += "#endif /* __%s__ */\n" % self.__namespace__
|
|
# generate implementation
|
|
if ( self.__langopts__["writefiles"].lower() == "true" ):
|
|
impstr = "#include \"%s.h\"\n\n" % self.__namespace__
|
|
impstr += "\n"
|
|
# generate the constructor
|
|
impstr += "void %s::%s(void)\n{\n" % (self.__namespace__, self.__namespace__)
|
|
# assignments happen in the constructor, not the class def
|
|
for var in self.__vars__ :
|
|
impstr += "%s\n" % str(var)
|
|
impstr += "}\n"
|
|
for func in self.__funcs__:
|
|
impstr += "%s\n" % str(func)
|
|
if ( self.__langopts__["writefiles"].lower() == "true" ):
|
|
# write the definition to the header file
|
|
ofile = open(("%s.h" % self.__namespace__), "w")
|
|
ofile.write(hdrstr)
|
|
ofile.close()
|
|
# write the implementation to the C file
|
|
ofile = open(("%s.c" % self.__namespace__), "w")
|
|
ofile.write(impstr)
|
|
ofile.close()
|
|
return ""
|
|
return hdrstr+"\n"+impstr
|
|
def __init__ (self, langopts, namespace="PTC"):
|
|
self.__namespace__ = namespace
|
|
self.__langopts__ = { "deftype" : "double",
|
|
"defrettype" : "double",
|
|
"writefiles" : "false" }
|
|
self.__funclist__ = []
|
|
self.__varlist__ = []
|
|
self.__subs__ = {}
|
|
for key in langopts.keys():
|
|
self.__langopts__[key] = langopts[key]
|
|
return
|
|
def convertFile (self, data):
|
|
for item in data:
|
|
debugmsg("Processing item %s" % (item))
|
|
if ( item[0] == "VAR" ):
|
|
self.__varlist__.append( CPPConverter.CPPVariable(item[1],
|
|
item[2], self.__langopts__["deftype"], self.__namespace__ ))
|
|
else:
|
|
self.__funclist__.append( CPPConverter.CPPFunction(self.__namespace__,
|
|
self.__langopts__["deftype"], self.__langopts__["defrettype"],
|
|
item[1], item[2][0], item[2][1]))
|
|
for func in self.__funclist__ :
|
|
func.replaceVars(self.__varlist__)
|
|
defclass = CPPConverter.CPPClass(self.__namespace__, self.__varlist__, self.__funclist__, self.__langopts__)
|
|
return defclass.toASCII()
|
|
def supportedTranslations (self):
|
|
return "The entire C++ math.h library is supported. You are almost guaranteed 100% conversion."
|
|
|
|
class CConverter:
|
|
class CVariable:
|
|
def __init__ (self, name, initValue, baseType, namespace):
|
|
self.__name__ = name
|
|
self.__value__ = initValue
|
|
self.__vartype__ = baseType
|
|
self.__namespace__ = namespace
|
|
def __str__ (self, defonly = False):
|
|
retStr = ""
|
|
if ( isinstance(self.__value__, list) or isinstance(self.__value__, tuple) ):
|
|
if ( defonly ):
|
|
return "%s %s_%s[%d];" % (self.__vartype__, self.__namespace__, self.__name__, len(self.__value__))
|
|
retStr = "%s %s_%s[%d] = {" % (self.__vartype__, self.__namespace__, self.__name__, len(self.__value__))
|
|
for i in self.__value__:
|
|
retStr += "%s, " % i
|
|
retStr += "};"
|
|
else:
|
|
if ( defonly ):
|
|
return "%s %s_%s;" % (self.__vartype__, self.__namespace__, self.__name__)
|
|
retStr = "%s %s_%s = %s;" % (self.__vartype__, self.__namespace__, self.__name__, self.__value__)
|
|
return retStr
|
|
class CFunction:
|
|
def __init__ (self, namespace, argtype, rettype, name, arguments, code):
|
|
self.__rettype__ = rettype
|
|
self.__argtype__ = argtype
|
|
self.__name__ = name
|
|
self.__namespace__ = namespace
|
|
self.__args__ = arguments
|
|
self.__code__ = code
|
|
def replaceVars (self, varlist):
|
|
"""Find any instance of a variable in our code that is not in our argument list,
|
|
and is a member variable of our parent class"""
|
|
codeCopy = self.__code__
|
|
ourlist = cloneList(MATHSYMS)
|
|
ourlist.append(",")
|
|
ourlist.append(" ")
|
|
for var in varlist :
|
|
if ( var.__name__ in self.__args__ ):
|
|
continue
|
|
for symbol in ourlist:
|
|
toreplace = "%s%s" % (symbol, var.__name__)
|
|
replacer = "%s_%s" % (self.__namespace__, var.__name__)
|
|
debugmsg("Searching for \" %s\" in code block %s" % (toreplace, codeCopy))
|
|
if ( toreplace in codeCopy ):
|
|
debugmsg("Replacing \"%s\" with \"%s\" in %s" % (toreplace, replacer, codeCopy))
|
|
codeCopy = codeCopy.replace(toreplace, replacer)
|
|
self.__code__ = codeCopy
|
|
def __str__ (self, defonly=False):
|
|
str = "%s %s_%s (" % (self.__rettype__, self.__namespace__, self.__name__)
|
|
argcnt = 0
|
|
for item in self.__args__:
|
|
if ( argcnt > 0 ):
|
|
str += ", "
|
|
str += "%s %s" % (self.__argtype__, item)
|
|
argcnt += 1
|
|
str += ")"
|
|
if ( defonly ):
|
|
return str+";"
|
|
str += "\n{\n\treturn (%s);\n}\n" % (self.__code__)
|
|
return str
|
|
class CInclude:
|
|
def __init__ (self, namespace, variables, functions):
|
|
self.__namespace__ = namespace
|
|
self.__vars__ = variables
|
|
self.__funcs__ = functions
|
|
def toASCII (self, writefiles):
|
|
hdrstr = ""
|
|
impstr = ""
|
|
# generate definition
|
|
hdrstr = "#ifndef __%s__\n" % self.__namespace__
|
|
hdrstr += "#define __%s__\n" % self.__namespace__
|
|
hdrstr += "#include <math.h>\n\n"
|
|
for var in self.__vars__ :
|
|
hdrstr += "extern %s\n" % (var.__str__(True))
|
|
hdrstr += "\n"
|
|
for func in self.__funcs__:
|
|
hdrstr += "extern %s\n" % func.__str__(True)
|
|
hdrstr += "#endif /* __%s__ */" % self.__namespace__
|
|
# generate implementation
|
|
if ( writefiles.lower() == "true" ):
|
|
impstr = "#include \"%s.h\"\n\n" % self.__namespace__
|
|
for var in self.__vars__ :
|
|
impstr += "%s\n" % (str(var))
|
|
impstr += "\n"
|
|
for func in self.__funcs__:
|
|
impstr += "%s\n" % str(func)
|
|
if ( writefiles.lower() == "true" ):
|
|
# write the definition to the header file
|
|
ofile = open(("%s.h" % self.__namespace__), "w")
|
|
ofile.write(hdrstr)
|
|
ofile.close()
|
|
# write the implementation to the C file
|
|
ofile = open(("%s.c" % self.__namespace__), "w")
|
|
ofile.write(impstr)
|
|
ofile.close()
|
|
return ""
|
|
return hdrstr+"\n"+impstr
|
|
def __init__ (self, langopts, namespace="PTC"):
|
|
self.__namespace__ = namespace
|
|
self.__langopts__ = { "deftype" : "double",
|
|
"defrettype" : "double",
|
|
"writefiles" : "false" }
|
|
self.__funclist__ = []
|
|
self.__varlist__ = []
|
|
# thankfully C doesn't require any substitutions; we just include math.h and
|
|
# everything is groovy.
|
|
self.__subs__ = {}
|
|
for key in langopts.keys():
|
|
self.__langopts__[key] = langopts[key]
|
|
return
|
|
def convertFile (self, data):
|
|
# forcibly process all the variables first, then do the functions.
|
|
# We do it in this specific order so we can do some variable replacement.
|
|
for item in data:
|
|
debugmsg("Processing item %s" % (item))
|
|
if ( item[0] == "VAR" ):
|
|
self.__varlist__.append( CConverter.CVariable(item[1],
|
|
item[2], self.__langopts__["deftype"], self.__namespace__ ))
|
|
else:
|
|
self.__funclist__.append( CConverter.CFunction(self.__namespace__,
|
|
self.__langopts__["deftype"], self.__langopts__["defrettype"],
|
|
item[1], item[2][0], item[2][1]))
|
|
for func in self.__funclist__ :
|
|
func.replaceVars(self.__varlist__)
|
|
defclass = CConverter.CInclude(self.__namespace__, self.__varlist__, self.__funclist__)
|
|
return defclass.toASCII(self.__langopts__["writefiles"])
|
|
def supportedTranslations (self):
|
|
return "The entire C math.h library is supported. You are almost guaranteed 100% conversion."
|
|
|
|
class PythonConverter:
|
|
# what the class needs to know:
|
|
# - the name of the class being generated. All data found in the .pcf file is
|
|
# placed into a generated class of the given name. Variables are listed as
|
|
# instance variables, and functions are of course generated as member functions.
|
|
# - list of functions
|
|
# - list of variables
|
|
class PythonVariable:
|
|
def __init__ (self, name, initValue, baseType):
|
|
# note that initValue can be a list when the baseType constructor
|
|
# requires more than one argument
|
|
self.__name__ = name
|
|
self.__value__ = initValue
|
|
self.__vartype__ = baseType
|
|
def __str__ (self):
|
|
retStr = ""
|
|
if ( isinstance(self.__value__, list) or isinstance(self.__value__, tuple) ):
|
|
retStr = "%s = %s(" % (self.__name__, self.__vartype__)
|
|
for i in self.__value__:
|
|
retStr += "%s, " % i
|
|
retStr += ")"
|
|
else:
|
|
retStr = "__%s__ = %s(%s)" % (self.__name__, self.__vartype__, self.__value__)
|
|
return retStr
|
|
class PythonFunction:
|
|
def __init__ (self, name, arguments, code):
|
|
self.__name__ = name
|
|
self.__args__ = arguments
|
|
self.__code__ = code
|
|
def replaceVars (self, varlist):
|
|
"""Find any instance of a variable in our code that is not in our argument list,
|
|
and is a member variable of our parent class"""
|
|
codeCopy = self.__code__
|
|
ourlist = cloneList(MATHSYMS)
|
|
ourlist.append(",")
|
|
ourlist.append(" ")
|
|
for var in varlist :
|
|
if ( var.__name__ in self.__args__ ):
|
|
continue
|
|
for symbol in ourlist:
|
|
toreplace = "%s%s" % (symbol, var.__name__)
|
|
replacer = "self.__%s__" % (var.__name__)
|
|
debugmsg("Searching for \" %s\" in code block %s" % (toreplace, codeCopy))
|
|
if ( toreplace in codeCopy ):
|
|
debugmsg("Replacing \"%s\" with \"%s\" in %s" % (toreplace, replacer, codeCopy))
|
|
codeCopy = codeCopy.replace(toreplace, replacer)
|
|
self.__code__ = codeCopy
|
|
def __str__ (self):
|
|
str = "def %s (self, " % (self.__name__)
|
|
argcnt = 0
|
|
for item in self.__args__:
|
|
if ( argcnt > 0 ):
|
|
str += ", "
|
|
str += "%s" % (item)
|
|
argcnt += 1
|
|
str += "): return (%s)" % (self.__code__)
|
|
return str
|
|
class PythonClass:
|
|
def __init__ (self, name, variables, functions):
|
|
self.__name__ = name
|
|
self.__vars__ = variables
|
|
self.__funcs__ = functions
|
|
def toASCII (self, tabstop):
|
|
# return all elements in a string suitable for printing to stdout
|
|
# "tabstop" specifies the tabstop to use. Pass "\t" or "" with a number of
|
|
# spaces per tabstop. There will be one additional space per tabstop
|
|
# because it makes it easier for me to write that way.
|
|
retstr = "import math\n"
|
|
retstr += "class %s:\n" % self.__name__
|
|
retstr += "%s def __init__(self):\n" % (tabstop)
|
|
for var in self.__vars__ :
|
|
retstr += "%s self.%s\n" % ((tabstop*2), str(var))
|
|
retstr += "%s return\n" % ((tabstop *2))
|
|
for func in self.__funcs__:
|
|
retstr += "%s %s\n" % (tabstop, str(func))
|
|
return retstr
|
|
def __init__ (self, langopts, className="PowerToyCapsule"):
|
|
self.__className__ = className
|
|
self.__langopts__ = langopts
|
|
self.__funclist__ = []
|
|
self.__varlist__ = []
|
|
self.__subs__ = {}
|
|
for item in dir(math):
|
|
if ( "__" in item ):
|
|
continue
|
|
self.__subs__[item] = "math.%s" % item
|
|
return
|
|
def convertFile (self, data):
|
|
# forcibly process all the variables first, then do the functions.
|
|
# We do it in this specific order so we can do some variable replacement.
|
|
for item in data:
|
|
debugmsg("Processing item %s" % (item))
|
|
if ( item[0] == "VAR" ):
|
|
if ( "." in item[2] ):
|
|
self.__varlist__.append( PythonConverter.PythonVariable(item[1], item[2], "float") )
|
|
else:
|
|
self.__varlist__.append( PythonConverter.PythonVariable(item[1], item[2], "int") )
|
|
else:
|
|
self.__funclist__.append( PythonConverter.PythonFunction(item[1], item[2][0], item[2][1]) )
|
|
for func in self.__funclist__ :
|
|
func.replaceVars(self.__varlist__)
|
|
defclass = PythonConverter.PythonClass(self.__className__, self.__varlist__, self.__funclist__)
|
|
return defclass.toASCII(" ")
|
|
def supportedTranslations (self):
|
|
retstr = ""
|
|
for call in self.__subs__.keys():
|
|
retstr += "%s " % call
|
|
return retstr
|
|
|
|
############################# PROCESSOR CLASSES ###################
|
|
|
|
class GenericProcessor:
|
|
def __init__ (self, converters):
|
|
# "converters" is a dictionary of language types and the corresponding classes that
|
|
# convert for that type
|
|
self.__converters__ = converters
|
|
|
|
def convertFile (self, filename, outfile, language, ignoreErrors, recovery, translate, langopts):
|
|
debugmsg("convertFile is not implemented in the GenericConverter base class.")
|
|
return 1
|
|
|
|
class MSPowerToyProcessor(GenericProcessor):
|
|
def convertFile (self, filename, outfile, language, ignoreErrors, recovery, translate, langopts):
|
|
global MATHSYMS
|
|
# get the right converter for our language type
|
|
convclass = self.__converters__[language]
|
|
converter = convclass(langopts)
|
|
data = []
|
|
if ( filename == "-" ):
|
|
ifile = sys.__stdin__
|
|
else:
|
|
ifile = open(filename, "rb")
|
|
if ( not ifile ):
|
|
generalerr("Unable to open input file %s, aborting." % filename)
|
|
linenum = 1
|
|
line = ifile.readline()
|
|
while ( line and (len(line) != 0) and line != "\x00"):
|
|
debugmsg("Operating on line %d with length %d : %s" % (linenum, len(line), line))
|
|
dataTYPE = ""
|
|
dataNAME = ""
|
|
dataVALUELITERAL = 0
|
|
dataVALUELIST = [[], ""]
|
|
# if there are parenthesis present on the LEFT side of the =, then the line is a function
|
|
# replace the BS nulls that PowerToyCalc puts into these files for some reason
|
|
line = line.replace("\x00", "").replace("\r", "").replace("\n", "")
|
|
# strip out the parts
|
|
parts = line.split("=")
|
|
debugmsg("Operating on parts %s" % parts)
|
|
# strip out all arithmetic operators and spaces, and drop the bare contents into a
|
|
# temporary string
|
|
if ( len(parts) > 1 ):
|
|
temp = parts[1]
|
|
for oper in MATHSYMS:
|
|
temp = temp.replace(oper, "")
|
|
# this final string will be used to test if any of the data in it is non-constant
|
|
# e.g., are there just numbers, or are there alphanumerics in there too?
|
|
temp = temp.replace(" ", "")
|
|
else:
|
|
sys.__stderr__.write("%s : %d : syntax error : no value in variable assignment : %s\n" \
|
|
% (filename, linenum, line))
|
|
if ( ignoreErrors ):
|
|
sys.__stderr__.write("%s : %d : defaulting value to 0 to continue\n" % (filename, linenum))
|
|
temp = "0"
|
|
else:
|
|
if ( ifile != sys.__stdin__ ):
|
|
ifile.close()
|
|
return 1
|
|
# if they are present on the right side of the equals, it's a variable
|
|
# If the variable has non-constant values to the right of =, then define that as a
|
|
# function with no arguments that returns the contents of the right side of the
|
|
# equals
|
|
if ( temp.isdigit() ):
|
|
# it's a variable, regardless of the presence of (), because it's just digits
|
|
dataTYPE = "VAR"
|
|
dataNAME = parts[0].replace(" ", "")
|
|
dataVALUELITERAL = parts[1].replace(" ", "")
|
|
elif ( "(" in parts[0] and ")" in parts[0] ):
|
|
# it's a function (though it may have been defined as a variable with non-constant
|
|
# values, such as the results of other functions, in PowerToyCalc. We define such
|
|
# variables as funtions in the converter because they may have data in them
|
|
# that needs to be re-calculated every time they are used, and a function is the
|
|
# only way to do that. We're actually beyond PowerToyCalc in that, because
|
|
# the user has to manually change a variable in PowerToyCalc to update it, we
|
|
# don't.)
|
|
dataTYPE = "FUNC"
|
|
dataNAME = parts[0].split("(")[0].split(")")[0].replace(" ", "")
|
|
dataVALUELIST[0] = parts[0].replace(" ", "").split("(")[1].replace(")", "").split(",")
|
|
if ( translate ):
|
|
dataVALUELIST[1] = converter.convertCalls(parts[1])
|
|
else:
|
|
dataVALUELIST[1] = parts[1]
|
|
else:
|
|
# if it didn't fit into the two top categories, I dunno WTF it is
|
|
sys.__stderr__.write("%s : %d : syntax error, no value in variable assignment : %s\n" % (filename, linenum, line))
|
|
# unless we're ignoring all errors, stop processing!
|
|
if ( ignoreErrors ):
|
|
if ( recovery ):
|
|
sys.__stderr__.write("%s : %d : defaulting value to 0 to continue\n" % (filename, linenum))
|
|
dataTYPE = "VAR"
|
|
dataNAME = parts[0].replace(" ", "")
|
|
dataVALUELITERAL = "0"
|
|
else:
|
|
line = ifile.readline()
|
|
continue
|
|
else:
|
|
if ( ifile != sys.__stdin__ ):
|
|
ifile.close()
|
|
return 1
|
|
if ( dataTYPE == "FUNC" ):
|
|
newlist=[dataTYPE, dataNAME, dataVALUELIST]
|
|
else:
|
|
newlist=[dataTYPE, dataNAME, dataVALUELITERAL]
|
|
debugmsg("Adding new list : %s" % newlist)
|
|
data.append(newlist)
|
|
line = ifile.readline()
|
|
linenum += 1
|
|
# out of the main conversion loop
|
|
if ( ifile != sys.__stdin__ ):
|
|
ifile.close()
|
|
if ( outfile == "-" ):
|
|
sys.__stdout__.write(converter.convertFile(data))
|
|
else:
|
|
try:
|
|
ofile = file(outfile, "w")
|
|
ofile.write(converter.convertFile(data))
|
|
ofile.close()
|
|
except IOError, e:
|
|
sys.__stderr__.write("Failed to open output file %s : %s" % (outfile, e))
|
|
return 1
|
|
return 0
|
|
|
|
################ GLOBALS ########################
|
|
DEBUG=False
|
|
MATHSYMS=["*", "+", "-", "=", "/", ">>", "<<", "%", "(", ")"]
|
|
CONVERTERS = { "python" : PythonConverter,
|
|
"C" : CConverter,
|
|
"C++" : CPPConverter }
|
|
PROCESSORS = { "mspcf" : [MSPowerToyProcessor, "MS Powertoy Calculator variable / function save data"]}
|
|
SHORTOPTS = "hf:l:t:o:erTSdO:"
|
|
LONGOPTS = ["help", "filename=", "language=", "filetype=",
|
|
"outfile=", "ignore-err", "recovery", "translate",
|
|
"supp-trans", "debug", "langopts="]
|
|
USAGE = \
|
|
"""Interpret mathematical formulae from various formats (such as
|
|
MS PowerToy Calculator saves) to usable source code for one of
|
|
a number of languages.
|
|
|
|
usage: pcfconv.py (opts)
|
|
options:
|
|
-h | --help : print this help
|
|
-f | --filename : filename to read from. If unspecified,
|
|
stdin is read by default.
|
|
-o | --outfile : filename to write to. If unspecified,
|
|
the text is written to stdout.
|
|
-t | --filetype : type of file being processed. If unspecified,
|
|
MS Powertoy Calculator is the default.
|
|
The interpreter cannot currently accurately
|
|
determine filetypes on its own.
|
|
-l | --language : language to write out. If unspecified,
|
|
python is the default output language.
|
|
-e | --ignore-err : ignore syntax errors in the source file
|
|
when one is encountered and continue
|
|
processing (note that the error will
|
|
still be reported to stderr)
|
|
-r | --recovery : Attempt to recover from any ignored
|
|
errors, in the best way the interpreter
|
|
can see fit. This could be dangerous.
|
|
Don't blame the interpreter if this
|
|
results in a divide by zero, you've
|
|
been warned!
|
|
-T | --translate : Setting this option will force the interpreter
|
|
to attempt to translate calls to math functions,
|
|
such as cos, acos, tan and log10, etc, to the
|
|
internal math functions of whatever host language
|
|
you're converting to. If this is unset - which
|
|
is the default - such calls will be left as-is,
|
|
and if necessary, you will have to change them
|
|
yourself. Note that some functions may not
|
|
be interpreted properly by your host language.
|
|
-S | --supp-trans : Print out what function translations (handled
|
|
by the -T option) are supported by the converter
|
|
for your host language
|
|
-d | --debug : Print debug output (normally supressed.)
|
|
-O | --langopts : A comma separated list of options for the
|
|
language processor for your given language,
|
|
with each option conforming to option=value. For
|
|
a list of options for each language processor,
|
|
refer to the complete documentation.
|
|
|
|
Languages currently supported: \n"""
|
|
USAGE += "\t" + (" ".join(CONVERTERS.keys())) + "\n"
|
|
USAGE += """Filetypes currently supported:\n"""
|
|
for proc in PROCESSORS.keys() :
|
|
USAGE += "\t%s : %s\n" % (proc, PROCESSORS[proc][1])
|
|
USAGE += """\npcfconv.py by Andrew Kesterson, 2008, released under the AKLabs
|
|
License. Go to http://www.aklabs.net/source/license.txt for the
|
|
full text of this license. Email the author andrew@aklabs.net for
|
|
all questions and bug reports.
|
|
"""
|
|
|
|
######################## MAIN LOGIC #############################
|
|
|
|
def main (argc, argv):
|
|
global DEBUG
|
|
infile = "-"
|
|
outfile = "-"
|
|
language = "python"
|
|
filetype = "mspcf"
|
|
ignoreErrors = False
|
|
recovery = False
|
|
translate = False
|
|
supp_trans = False
|
|
langopts = {}
|
|
opts = getopt.getopt(argv, SHORTOPTS, LONGOPTS)
|
|
for pair in opts[0]:
|
|
if ( pair[0] == "-h" or pair[0] == "--help" ):
|
|
sys.__stderr__.write(USAGE)
|
|
return 1
|
|
elif ( pair[0] == "-d" or pair[0] == "--debug" ):
|
|
DEBUG=True
|
|
elif ( pair[0] == "-l" or pair[0] == "--language" ):
|
|
language = pair[1]
|
|
elif ( pair[0] == "-f" or pair[0] == "--filename" ):
|
|
infile = pair[1]
|
|
elif ( pair[0] == "-o" or pair[0] == "--outfile" ):
|
|
outfile = pair[1]
|
|
elif ( pair[0] == "-e" or pair[0] == "--ignore-err" ):
|
|
ignoreErrors = True
|
|
elif ( pair[0] == "-r" or pair[0] == "--recovery" ):
|
|
recovery = True
|
|
elif ( pair[0] == "-t" or pair[0] == "--filetype" ):
|
|
filetype = pair[1]
|
|
elif ( pair[0] == "-T" or pair[0] == "--translate" ):
|
|
translate = True
|
|
elif ( pair[0] == "-S" or pair[0] == "--supp-trans" ):
|
|
supp_trans = True
|
|
elif ( pair[0] == "-O" or pair[0] == "--langopts" ):
|
|
splitopts = pair[1].split("=")
|
|
for i in range(0, len(splitopts)-1, 2):
|
|
#print "Assigning language option %s = %s" % (splitopts[i], splitopts[i+1])
|
|
langopts[splitopts[i]] = splitopts[i+1]
|
|
procclass = PROCESSORS[filetype][0]
|
|
proc = procclass(CONVERTERS)
|
|
if ( supp_trans ):
|
|
convclass = CONVERTERS[language]
|
|
conv = convclass(langopts)
|
|
print conv.supportedTranslations()
|
|
return 0
|
|
return proc.convertFile(infile, outfile, language, ignoreErrors, recovery, translate, langopts)
|
|
|
|
if ( __name__ == "__main__" ):
|
|
sys.exit(main(len(sys.argv[1:])-1, sys.argv[1:]))
|
|
|