-- TODO: suggest the convenience functions be put into Hint proper?moduleMueval.InterpreterwhereimportControl.Monad(when)importqualifiedControl.Exception(catch)importControl.Monad.Trans(liftIO)importSystem.Directory(copyFile,makeRelativeToCurrentDirectory,removeFile)importSystem.FilePath.Posix(takeFileName)importLanguage.Haskell.Interpreter.GHC(eval,newSession,reset,setImports,loadModules,setOptimizations,setUseLanguageExtensions,setInstalledModsAreInScopeQualified,typeChecks,typeOf,withSession,setTopLevelModules,Interpreter,InterpreterError,ModuleName,Optimizations(All))importqualifiedCodec.Binary.UTF8.StringasCodec(decodeString)importqualifiedSystem.IO.UTF8asUTF(putStr)importqualifiedMueval.Resources(limitResources)-- | From inside the Interpreter monad, print the String (presumably the result-- of interpreting something), but only print the first 1024 characters to avoid-- flooding. Lambdabot has a similar limit.say::String->Interpreter()say=liftIO.UTF.putStr.Codec.decodeString.take1024-- | Oh no, something has gone wrong. Call 'error' and then, as with 'say',-- print out a maximum of 1024 characters.printInterpreterError::InterpreterError->IO()printInterpreterError=error.take1024.("Oops... "++).show{- | The actual calling of Hint functionality. The heart of this just calls
'eval', but we do so much more - we disable Haskell extensions, turn on
optimizations, hide all packages, make sure one cannot call unimported
functions, typecheck (and optionally print it), set resource limits for this
thread, and do some error handling. -}interpreter::Bool->Bool->[ModuleName]->String->String->Interpreter()interpreterprtextsmoduleslflexpr=dosetUseLanguageExtensionsexts-- False by defaultsetOptimizationsAll-- Maybe optimization will make-- more programs-- terminate.reset-- Make sure nothing is availablesetInstalledModsAreInScopeQualifiedFalseletdoload=iflfl==""thenFalseelseTruewhendoload(liftIO$mvloadlfl)liftIOMueval.Resources.limitResourceswhendoload$doletlfl'=takeFileNamelflloadModules[lfl']-- We need to mangle the String to-- turn a filename into a-- modulesetTopLevelModules[(takeWhile(/='.')lfl')]setImportsmoduleswhenprt(say$expr++"\n")checks<-typeChecksexprifchecksthendoifprtthendosay=<<typeOfexprsay"\n"elsereturn()result<-evalexprsay$showresult++"\n"elseerror"Expression did not type check."-- | Wrapper around 'interpreter'; supplies a fresh GHC API session and-- error-handling. The arguments are simply passed on.interpreterSession::Bool-- ^ Whether to print inferred type->Bool-- ^ Whether to use GHC extensions->[ModuleName]-- ^ A list of modules we wish to be visible->String-- ^ A local file from which to grab definitions; an-- empty string is treated as no file.->String-- ^ The string to be interpreted as a Haskell expression->IO()-- ^ No real result, since printing is done deeper in-- the stack.interpreterSessionprtextsmdslflexpr=Control.Exception.catch(newSession>>=(flipwithSession)(interpreterprtextsmdslflexpr))(\_->docaselflof""->return()l->docanonfile<-(makeRelativeToCurrentDirectoryl)removeFile("/tmp/"++takeFileNamecanonfile)error"Expression did not compile.")mvload::FilePath->IO()mvloadlfl=docanonfile<-(makeRelativeToCurrentDirectorylfl)liftIO$copyFilecanonfile("/tmp/"++(takeFileNamecanonfile))