-- | Sifflet to abstract syntax tree for Python.-- Use Python module's pyPretty to pretty-print the result.moduleLanguage.Sifflet.Export.ToPython(PythonOptions(..),defaultPythonOptions,exprToPExpr,nameToPython,fixIdentifierChars,functionToPyDef,defToPy,functionsToPyModule,functionsToPrettyPy,exportPython)whereimportData.Char(isAlpha,isDigit,ord)importControl.Monad(unless)importData.Map((!))importSystem.Directory(copyFile,doesFileExist)importSystem.FilePath(replaceFileName)importLanguage.Sifflet.Export.ExporterimportLanguage.Sifflet.Export.PythonimportLanguage.Sifflet.Expr-- import Text.Sifflet.PrettyimportLanguage.Sifflet.UtilimportPaths_sifflet_lib-- Cabal-generated paths module-- | Options for Python export.-- Should probably include choices like Python2 or Python3;-- if statement or if expression. Right now, just a placeholder.dataPythonOptions=PythonOptionsderiving(Eq,Show)defaultPythonOptions::PythonOptionsdefaultPythonOptions=PythonOptions-- A lot of these are "pass-through" -- simplify: ***exprToPExpr::Expr->ExprexprToPExprexpr=caseexprofEUndefined->EUndefined-- was var "undefined"ESymbol_->exprEBool_->exprECharc->EString[c]-- Python does not distinguish char from strENumber_->exprEString_->exprEIfcondactionaltAction->-- EIf here represents a Python if *expression*:-- value if test else altvalue-- not the familiar if *statement*!EIf(exprToPExprcond)(exprToPExpraction)(exprToPExpraltAction)EListexprs->ECall(Symbol"li")(mapexprToPExprexprs)ELambdaxbody->ELambdax(EGroupbody)ECall(Symbolfname)args->-- Python distinguishes between functions and operatorscasenameToPythonfnameofLeftop->caseargsof[left,right]->EOpop(EGroup(exprToPExprleft))(EGroup(exprToPExprright))_->error"exprToPExpr: operation does not have 2 operands"Rightpname->-- I don't think we need (a) for each arg a-- since they are separated by commasECall(Symbolpname)(mapexprToPExprargs)_->errcats["exprToPExpr: extended expr:",showexpr]-- | Convert Sifflet name (of a function) to Python operator (Left)-- or function name (Right)nameToPython::String->EitherOperatorStringnameToPythonname=letoperoname=Left$operatorTable!onameincasenameof"+"->oper"+""-"->oper"-""*"->oper"*""div"->oper"//""mod"->oper"%""/"->oper"/"-- invalid for integers in Python 2!"=="->oper"==""/="->oper"!="">"->oper">"">="->oper">=""<"->oper"<""<="->oper"<=""add1"->Right"add1""sub1"->Right"sub1""zero?"->Right"eqZero""positive?"->Right"gtZero""negative?"->Right"ltZero""null"->Right"null""head"->Right"head""tail"->Right"tail"":"->Right"cons"_->Right(fixIdentifierCharsname)-- | Remove characters that are not valid in a Python identifier,-- and in some cases, insert other characters to show what's missingfixIdentifierChars::String->StringfixIdentifierChars=letfixs=casesof[]->[]c:cs->ifisAlphac||isDigitc||c=='_'thenc:fixcselsecasecof'?'->"_QUESTION_"++fixcs-- other cases can be inserted here_->"_CHR"++show(ordc)++"_"++fixcsinfix-- | Create a Python def statement from a Sifflet function.-- Minimally parenthesized.functionToPyDef::Function->PStatementfunctionToPyDef=defToPy.functionToDefdefToPy::FunctionDefTuple->PStatementdefToPy(fname,paramNames,_,_,body)=fun(fixIdentifierCharsfname)paramNames((simplifyExprpyRules)(exprToPExprbody))pyRules::[Expr->Expr]pyRules=commonRulesForSimplifyingExprsfunctionsToPyModule::Functions->PModulefunctionsToPyModule(Functionsfs)=PModule(mapfunctionToPyDeffs)functionsToPrettyPy::Functions->StringfunctionsToPrettyPy=pyPretty.functionsToPyModuleexportPython::PythonOptions->ExporterexportPython_optionsfuncspath=letheader="# File: "++path++"\n# Generated by the Sifflet->Python exporter.\n\n"++"from sifflet import *\n\n"libDest=replaceFileNamepath"sifflet.py"indo{libDestExists<-doesFileExistlibDest;unlesslibDestExists(do-- copy sifflet.py to the same directory as path{libSrc<-pythonLibSiffletPath;copyFilelibSrclibDest});writeFilepath(header++(functionsToPrettyPyfuncs))}pythonLibSiffletPath::IOFilePathpythonLibSiffletPath=getDataFileName"sifflet.py"