| 1 | {-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances -fffi #-} |
|---|
| 2 | |
|---|
| 3 | {-| |
|---|
| 4 | Public API for the Pugs system. |
|---|
| 5 | |
|---|
| 6 | > Dance all ye joyful, now dance all together! |
|---|
| 7 | > Soft is the grass, and let foot be like feather! |
|---|
| 8 | > The river is silver, the shadows are fleeting; |
|---|
| 9 | > Merry is May-time, and merry our meeting. |
|---|
| 10 | |
|---|
| 11 | -} |
|---|
| 12 | |
|---|
| 13 | module Pugs ( |
|---|
| 14 | module Pugs, |
|---|
| 15 | Command(..), |
|---|
| 16 | banner, |
|---|
| 17 | liftSTM, |
|---|
| 18 | printCommandLineHelp, |
|---|
| 19 | intro, |
|---|
| 20 | initializeShell, |
|---|
| 21 | getCommand, |
|---|
| 22 | pretty, |
|---|
| 23 | printInteractiveHelp, |
|---|
| 24 | ) where |
|---|
| 25 | import Pugs.AST |
|---|
| 26 | import Pugs.CodeGen |
|---|
| 27 | import Pugs.Config |
|---|
| 28 | import Pugs.Embed |
|---|
| 29 | import Pugs.Eval |
|---|
| 30 | import Pugs.External |
|---|
| 31 | import Pugs.Help |
|---|
| 32 | import Pugs.Internals |
|---|
| 33 | import Pugs.Monads |
|---|
| 34 | import Pugs.Parser.Program |
|---|
| 35 | import Pugs.Pretty |
|---|
| 36 | import Pugs.Run |
|---|
| 37 | import Pugs.Shell |
|---|
| 38 | import Pugs.Types |
|---|
| 39 | import Data.IORef |
|---|
| 40 | import qualified Data.Map as Map |
|---|
| 41 | import qualified System.FilePath as FilePath (combine, splitFileName) |
|---|
| 42 | import Control.Timeout |
|---|
| 43 | |
|---|
| 44 | {-| |
|---|
| 45 | The entry point of Pugs. Uses 'Pugs.Run.runWithArgs' to normalise the command-line |
|---|
| 46 | arguments and pass them to 'run'. |
|---|
| 47 | -} |
|---|
| 48 | pugsMain :: IO () |
|---|
| 49 | pugsMain = do |
|---|
| 50 | let ?debugInfo = Nothing |
|---|
| 51 | timeout <- getEnv "PUGS_TIMEOUT" |
|---|
| 52 | case timeout of |
|---|
| 53 | Just str | [(t, _)] <- reads str -> do |
|---|
| 54 | addTimeout t (hPutStrLn stderr "*** TIMEOUT" >> _exit 1) |
|---|
| 55 | return () |
|---|
| 56 | _ -> return () |
|---|
| 57 | mainWith run |
|---|
| 58 | |
|---|
| 59 | foreign import ccall unsafe _exit :: Int -> IO () |
|---|
| 60 | |
|---|
| 61 | defaultProgramName :: String |
|---|
| 62 | defaultProgramName = "<interactive>" |
|---|
| 63 | |
|---|
| 64 | runFile :: String -> IO () |
|---|
| 65 | runFile file = do |
|---|
| 66 | withArgs [file] pugsMain |
|---|
| 67 | |
|---|
| 68 | run :: [String] -> IO () |
|---|
| 69 | run xs = let ?debugInfo = Nothing in run' xs |
|---|
| 70 | |
|---|
| 71 | -- see also Run/Args.hs |
|---|
| 72 | run' :: (?debugInfo :: DebugInfo) => [String] -> IO () |
|---|
| 73 | run' ("-d":rest) = do |
|---|
| 74 | info <- newDebugInfo |
|---|
| 75 | let ?debugInfo = info |
|---|
| 76 | run' rest |
|---|
| 77 | run' ("-l":rest) = run' rest |
|---|
| 78 | run' ("-w":rest) = run' rest |
|---|
| 79 | run' ("-I":_:rest) = run' rest |
|---|
| 80 | |
|---|
| 81 | -- XXX should raise an error here: |
|---|
| 82 | -- run ("-I":[]) = do |
|---|
| 83 | -- print "Empty -I" |
|---|
| 84 | |
|---|
| 85 | run' ("-h":_) = printCommandLineHelp |
|---|
| 86 | run' ("-V":_) = printConfigInfo [] |
|---|
| 87 | run' ("-V:":item:_) = printConfigInfo [item] |
|---|
| 88 | run' ("-v":_) = banner |
|---|
| 89 | |
|---|
| 90 | -- turn :file: and "-e":frag into a common subroutine/token |
|---|
| 91 | run' ("-c":"-e":prog:_) = doCheck "-e" prog |
|---|
| 92 | run' ("-c":file:_) = readFile file >>= doCheck file |
|---|
| 93 | |
|---|
| 94 | -- -CPIL1.Perl5 outputs PIL formatted as Perl 5. |
|---|
| 95 | run' ("-C":backend:args) | (== map toLower backend) `any` ["js","perl5","js-perl5"] = do |
|---|
| 96 | exec <- getArg0 |
|---|
| 97 | doHelperRun backend ("--compile-only":("--pugs="++exec):args) |
|---|
| 98 | run' ("-C":backend:"-e":prog:_) = doCompileDump backend "-e" prog |
|---|
| 99 | run' ("-C":backend:file:_) = readFile file >>= doCompileDump backend file |
|---|
| 100 | |
|---|
| 101 | run' ("-B":backend:_) | (== map toLower backend) `any` ["js","perl5","js-perl5","redsix"] = do |
|---|
| 102 | exec <- getArg0 |
|---|
| 103 | args <- getArgs |
|---|
| 104 | doHelperRun backend (("--pugs="++exec):args) |
|---|
| 105 | run' ("-B":backend:"-e":prog:_) = doCompileRun backend "-e" prog |
|---|
| 106 | run' ("-B":backend:file:_) = readFile file >>= doCompileRun backend file |
|---|
| 107 | |
|---|
| 108 | run' ("--external":mod:"-e":prog:_) = doExternal mod "-e" prog |
|---|
| 109 | run' ("--external":mod:file:_) = readFile file >>= doExternal mod file |
|---|
| 110 | |
|---|
| 111 | run' ("-e":prog:args) = do doRun "-e" args prog |
|---|
| 112 | -- -E is like -e, but not accessible as a normal parameter and used only |
|---|
| 113 | -- internally: |
|---|
| 114 | -- "-e foo bar.pl" executes "foo" with @*ARGS[0] eq "bar.pl", |
|---|
| 115 | -- "-E foo bar.pl" executes "foo" and then bar.pl. |
|---|
| 116 | -- XXX - Wrong -- Need to preserve environment across -E runs |
|---|
| 117 | run' ("-E":prog:rest) = run' ("-e":prog:[]) >> run' rest |
|---|
| 118 | run' ("-":args) = do doRun "-" args =<< readStdin |
|---|
| 119 | run' (file:args) = readFile file >>= doRun file args |
|---|
| 120 | run' [] = do |
|---|
| 121 | isTTY <- hIsTerminalDevice stdin |
|---|
| 122 | if isTTY |
|---|
| 123 | then do banner >> intro >> repLoop |
|---|
| 124 | else run' ["-"] |
|---|
| 125 | |
|---|
| 126 | readStdin :: IO String |
|---|
| 127 | readStdin = do |
|---|
| 128 | eof <- isEOF |
|---|
| 129 | if eof then return [] else do |
|---|
| 130 | ch <- getChar |
|---|
| 131 | rest <- readStdin |
|---|
| 132 | return (ch:rest) |
|---|
| 133 | |
|---|
| 134 | repLoop :: IO () |
|---|
| 135 | repLoop = initializeShell $ do |
|---|
| 136 | tvEnv <- io . newTVarIO . noEnvDebug =<< io (tabulaRasa defaultProgramName) |
|---|
| 137 | fix $ \loop -> do |
|---|
| 138 | command <- getCommand |
|---|
| 139 | let parseEnv f prog = do |
|---|
| 140 | env <- stm (readTVar tvEnv) |
|---|
| 141 | doParse env f defaultProgramName prog |
|---|
| 142 | resetEnv = do |
|---|
| 143 | env <- fmap noEnvDebug (tabulaRasa defaultProgramName) |
|---|
| 144 | stm (writeTVar tvEnv env) |
|---|
| 145 | if command == CmdQuit then io $ putStrLn "Leaving pugs." else do |
|---|
| 146 | io $ case command of |
|---|
| 147 | CmdLoad fn -> doLoad tvEnv fn |
|---|
| 148 | CmdRun opts prog -> doRunSingle tvEnv opts prog |
|---|
| 149 | CmdParse prog -> parseEnv pretty prog |
|---|
| 150 | CmdParseRaw prog -> parseEnv show prog |
|---|
| 151 | CmdHelp -> printInteractiveHelp |
|---|
| 152 | CmdReset -> resetEnv |
|---|
| 153 | _ -> return () |
|---|
| 154 | loop |
|---|
| 155 | |
|---|
| 156 | mainWith :: ([String] -> IO a) -> IO () |
|---|
| 157 | mainWith run = do |
|---|
| 158 | hSetBuffering stdout LineBuffering |
|---|
| 159 | -- when (isJust _DoCompile) $ do |
|---|
| 160 | -- writeIORef (fromJust _DoCompile) doCompile |
|---|
| 161 | runWithArgs run |
|---|
| 162 | globalFinalize |
|---|
| 163 | |
|---|
| 164 | -- convenience functions for GHCi |
|---|
| 165 | eval :: String -> IO () |
|---|
| 166 | eval prog = do |
|---|
| 167 | args <- getArgs |
|---|
| 168 | runProgramWith id (putStrLn . encodeUTF8 . pretty) defaultProgramName args (encodeUTF8 prog) |
|---|
| 169 | |
|---|
| 170 | parse :: String -> IO () |
|---|
| 171 | parse prog = do |
|---|
| 172 | env <- tabulaRasa defaultProgramName |
|---|
| 173 | doParse env (encodeUTF8 . pretty) "-" (encodeUTF8 prog) |
|---|
| 174 | |
|---|
| 175 | dump :: String -> IO () |
|---|
| 176 | dump = (doParseWith $ \env _ -> print $ envBody env) "-" |
|---|
| 177 | |
|---|
| 178 | globalFinalize :: IO () |
|---|
| 179 | globalFinalize = join $ readIORef _GlobalFinalizer |
|---|
| 180 | |
|---|
| 181 | dumpGlob :: String -> IO () |
|---|
| 182 | dumpGlob = (doParseWith $ \env _ -> do |
|---|
| 183 | glob <- stm . readMPad $ envGlobal env |
|---|
| 184 | print $ filterUserDefinedPad glob) "-" |
|---|
| 185 | |
|---|
| 186 | {-| |
|---|
| 187 | Create a \'blank\' 'Env' for our program to execute in. Of course, |
|---|
| 188 | 'prepareEnv' actually declares quite a few symbols in the environment, |
|---|
| 189 | e.g. \'\@\*ARGS\', \'\$\*PID\', \'\$\*ERR\' etc. |
|---|
| 190 | |
|---|
| 191 | ('Tabula rasa' is Latin for 'a blank slate'.) |
|---|
| 192 | -} |
|---|
| 193 | tabulaRasa :: String -> IO Env |
|---|
| 194 | tabulaRasa name = prepareEnv name [] |
|---|
| 195 | |
|---|
| 196 | doCheck :: FilePath -> String -> IO () |
|---|
| 197 | doCheck = doParseWith $ \_ name -> do |
|---|
| 198 | putStrLn $ name ++ " syntax OK" |
|---|
| 199 | |
|---|
| 200 | doExternal :: String -> FilePath -> String -> IO () |
|---|
| 201 | doExternal mod = doParseWith $ \env _ -> do |
|---|
| 202 | str <- externalize mod $ envBody env |
|---|
| 203 | putStrLn str |
|---|
| 204 | |
|---|
| 205 | doCompile :: String -> FilePath -> String -> IO String |
|---|
| 206 | doCompile backend = doParseWith $ \env file -> do |
|---|
| 207 | globRef <- stm $ do |
|---|
| 208 | glob <- readMPad $ envGlobal env |
|---|
| 209 | newMPad $ filterUserDefinedPad glob |
|---|
| 210 | codeGen backend file env{ envGlobal = globRef } |
|---|
| 211 | |
|---|
| 212 | initCompile :: IO () |
|---|
| 213 | initCompile = do |
|---|
| 214 | compPrelude <- getEnv "PUGS_COMPILE_PRELUDE" |
|---|
| 215 | let bypass = case compPrelude of |
|---|
| 216 | Nothing -> True |
|---|
| 217 | Just "" -> True |
|---|
| 218 | Just "0" -> True |
|---|
| 219 | _ -> False |
|---|
| 220 | setEnv "PUGS_COMPILE_PRELUDE" (if bypass then "0" else "") True |
|---|
| 221 | |
|---|
| 222 | doCompileDump :: String -> FilePath -> String -> IO () |
|---|
| 223 | doCompileDump backend file prog = do |
|---|
| 224 | initCompile |
|---|
| 225 | str <- doCompile backend' file prog |
|---|
| 226 | putStr str |
|---|
| 227 | where |
|---|
| 228 | backend' = capitalizeWord backend |
|---|
| 229 | capitalizeWord [] = [] |
|---|
| 230 | capitalizeWord (c:cs) = toUpper c:(map toLower cs) |
|---|
| 231 | |
|---|
| 232 | doCompileRun :: String -> FilePath -> String -> IO () |
|---|
| 233 | doCompileRun backend file prog = do |
|---|
| 234 | initCompile |
|---|
| 235 | str <- doCompile backend' file prog |
|---|
| 236 | evalEmbedded backend' str |
|---|
| 237 | where |
|---|
| 238 | backend' = capitalizeWord backend |
|---|
| 239 | capitalizeWord [] = [] |
|---|
| 240 | capitalizeWord (c:cs) = toUpper c:(map toLower cs) |
|---|
| 241 | |
|---|
| 242 | doHelperRun :: String -> [String] -> IO () |
|---|
| 243 | doHelperRun backend args = |
|---|
| 244 | case map toLower backend of |
|---|
| 245 | "js" -> if (args' == []) |
|---|
| 246 | then (doExecuteHelper "jspugs.pl" args) |
|---|
| 247 | else (doExecuteHelper "runjs.pl" args) |
|---|
| 248 | "perl5" -> doExecuteHelper "v6.pm" args |
|---|
| 249 | "js-perl5" -> doExecuteHelper "runjs.pl" (jsPerl5Args ++ args) |
|---|
| 250 | "redsix" -> doExecuteHelper "redsix" args |
|---|
| 251 | _ -> fail ("unknown backend: " ++ backend) |
|---|
| 252 | where |
|---|
| 253 | args' = f args |
|---|
| 254 | jsPerl5Args = words "--run=jspm --perl5" |
|---|
| 255 | f [] = [] |
|---|
| 256 | f (bjs:rest) | "-BJS" `isPrefixOf` map toUpper bjs = f rest |
|---|
| 257 | f ("-B":js:rest) | "JS" `isPrefixOf` map toUpper js = f rest |
|---|
| 258 | f (pugspath:rest) | "--pugs=" `isPrefixOf` pugspath = f rest |
|---|
| 259 | f (x:xs) = x:f xs |
|---|
| 260 | |
|---|
| 261 | doExecuteHelper :: FilePath -> [String] -> IO () |
|---|
| 262 | doExecuteHelper helper args = do |
|---|
| 263 | let searchPaths = concatMap (\x -> map (x++) suffixes) [["."], ["..", ".."], [getConfig "sourcedir"], [getConfig "sourcedir", "blib6", "pugs"], [getConfig "privlib", "auto", "pugs"], [getConfig "sitelib", "auto", "pugs"]] |
|---|
| 264 | mbin <- runMaybeT (findHelper searchPaths) |
|---|
| 265 | case mbin of |
|---|
| 266 | Just binary -> do |
|---|
| 267 | let (p, _) = FilePath.splitFileName binary |
|---|
| 268 | exitWith =<< executeFile' perl5 True (("-I" ++ p):binary:args) Nothing |
|---|
| 269 | _ -> fail ("Couldn't find helper program " ++ helper ++ " (searched in " ++ show (map (foldl1 FilePath.combine) searchPaths) ++ ")") |
|---|
| 270 | where |
|---|
| 271 | suffixes = |
|---|
| 272 | [ [] |
|---|
| 273 | , ["perl5", "PIL2JS"] -- sourcedir/perl5/PIL2JS/jspugs.pl |
|---|
| 274 | , ["perl5", "lib"] -- pugslibdir/perl5/lib/jspugs.pl |
|---|
| 275 | , ["misc", "pX", "Common", "redsix"] -- sourcedir/misc/pX/Common/redsix/redsix |
|---|
| 276 | ] |
|---|
| 277 | perl5 = getConfig "perl5_path" |
|---|
| 278 | findHelper :: [[FilePath]] -> MaybeT IO FilePath |
|---|
| 279 | findHelper [] = fail "Can't find anything" |
|---|
| 280 | findHelper (x:xs) = maybeFindFile file |
|---|
| 281 | `mplus` maybeFindFile (file ++ getConfig "exe_ext") |
|---|
| 282 | `mplus` findHelper xs |
|---|
| 283 | where |
|---|
| 284 | file = foldl1 FilePath.combine (x ++ [helper]) |
|---|
| 285 | maybeFindFile :: FilePath -> MaybeT IO FilePath |
|---|
| 286 | maybeFindFile pathname = do |
|---|
| 287 | dir <- liftIO $ getDirectoryContents path `catchIO` (const $ return []) |
|---|
| 288 | guard (filename `elem` dir) |
|---|
| 289 | return pathname |
|---|
| 290 | where |
|---|
| 291 | (path, filename) = FilePath.splitFileName pathname |
|---|
| 292 | |
|---|
| 293 | doParseWith :: (Env -> FilePath -> IO a) -> FilePath -> String -> IO a |
|---|
| 294 | doParseWith f name prog = do |
|---|
| 295 | env <- tabulaRasa name |
|---|
| 296 | f' $ parseProgram env{ envDebug = Nothing } name prog |
|---|
| 297 | where |
|---|
| 298 | f' env | Val err@(VError _ _) <- envBody env = do |
|---|
| 299 | hPutStrLn stderr $ pretty err |
|---|
| 300 | globalFinalize |
|---|
| 301 | exitFailure |
|---|
| 302 | f' env = f env name |
|---|
| 303 | |
|---|
| 304 | doParse :: Env -> (Exp -> String) -> FilePath -> String -> IO () |
|---|
| 305 | doParse env prettyFunc name prog = do |
|---|
| 306 | case envBody $ parseProgram env name prog of |
|---|
| 307 | (Val err@(VError _ _)) -> putStrLn $ pretty err |
|---|
| 308 | exp -> putStrLn $ prettyFunc exp |
|---|
| 309 | |
|---|
| 310 | doLoad :: TVar Env -> String -> IO () |
|---|
| 311 | doLoad env fn = do |
|---|
| 312 | runImperatively env (evaluate exp) |
|---|
| 313 | return () |
|---|
| 314 | where |
|---|
| 315 | exp = App (_Var "&require") Nothing [Val $ VStr fn] |
|---|
| 316 | |
|---|
| 317 | doRunSingle :: TVar Env -> RunOptions -> String -> IO () |
|---|
| 318 | doRunSingle menv opts prog = (`catchIO` handler) $ do |
|---|
| 319 | exp <- makeProper =<< parse |
|---|
| 320 | if exp == Noop then return () else do |
|---|
| 321 | env <- theEnv |
|---|
| 322 | rv <- runImperatively env (evaluate exp) |
|---|
| 323 | result <- case rv of |
|---|
| 324 | VControl (ControlContinuation env' val _) -> do |
|---|
| 325 | stm $ writeTVar menv env' |
|---|
| 326 | return val |
|---|
| 327 | _ -> return rv |
|---|
| 328 | printer env result |
|---|
| 329 | where |
|---|
| 330 | parse = do |
|---|
| 331 | env <- stm $ readTVar menv |
|---|
| 332 | return $ envBody $ parseProgram env defaultProgramName $ |
|---|
| 333 | (dropTrailingSemi prog) |
|---|
| 334 | dropTrailingSemi = reverse . |
|---|
| 335 | (\x -> ';' : (dropWhile (`elem` " \t\r\n;") x)) . |
|---|
| 336 | reverse |
|---|
| 337 | hasTrailingSemi = case f prog of ';':_ -> True; _ -> False |
|---|
| 338 | where f = dropWhile (`elem` " \t\r\n\f") . reverse |
|---|
| 339 | theEnv = do |
|---|
| 340 | ref <- if runOptSeparately opts |
|---|
| 341 | then (io . newTVarIO) =<< tabulaRasa defaultProgramName |
|---|
| 342 | else return menv |
|---|
| 343 | debug <- if runOptDebug opts |
|---|
| 344 | then newDebugInfo |
|---|
| 345 | else return Nothing |
|---|
| 346 | stm $ modifyTVar ref $ \e -> e{ envDebug = debug } |
|---|
| 347 | return ref |
|---|
| 348 | printer' = if runOptShowPretty opts then putStrLn . pretty else print |
|---|
| 349 | printer env = \val -> do |
|---|
| 350 | final <- runImperatively env (fromVal' val) |
|---|
| 351 | if hasTrailingSemi |
|---|
| 352 | then case final of (VError _ _) -> printer' final ; _ -> return () |
|---|
| 353 | else printer' final |
|---|
| 354 | makeProper exp = case exp of |
|---|
| 355 | Val err@(VError (VStr msg) _) |
|---|
| 356 | | runOptShowPretty opts |
|---|
| 357 | , any (== "Unexpected end of input") (lines msg) -> do |
|---|
| 358 | cont <- readline "....> " |
|---|
| 359 | case cont of |
|---|
| 360 | Just line -> do |
|---|
| 361 | doRunSingle menv opts (prog ++ ('\n':line)) |
|---|
| 362 | return Noop |
|---|
| 363 | _ -> fail $ pretty err |
|---|
| 364 | Val err@VError{} -> fail $ pretty err |
|---|
| 365 | _ | runOptSeparately opts -> return exp |
|---|
| 366 | App (Syn "block" [Val (VCode cv)]) invs args -> return $ |
|---|
| 367 | App (Syn "block" [Val (VCode cv{ subBody = makeDumpEnv (subBody cv) })]) invs args |
|---|
| 368 | _ -> return $ makeDumpEnv exp |
|---|
| 369 | |
|---|
| 370 | -- XXX Generalize this into structural folding |
|---|
| 371 | makeDumpEnv Noop = Syn "continuation" [] |
|---|
| 372 | makeDumpEnv (Stmts x Noop) = Stmts (Ann (Cxt cxtItemAny) x) (Syn "continuation" []) |
|---|
| 373 | makeDumpEnv (Stmts x exp) = Stmts x $ makeDumpEnv exp |
|---|
| 374 | makeDumpEnv (Ann ann exp) = Ann ann $ makeDumpEnv exp |
|---|
| 375 | makeDumpEnv (Sym x y z w exp) = Sym x y z w $ makeDumpEnv exp |
|---|
| 376 | makeDumpEnv exp = Stmts (Ann (Cxt cxtItemAny) exp) (Syn "continuation" []) |
|---|
| 377 | |
|---|
| 378 | handler (IOException ioe) | isUserError ioe = do |
|---|
| 379 | putStrLn "Internal error while running expression:" |
|---|
| 380 | putStrLn $ ioeGetErrorString ioe |
|---|
| 381 | handler err = do |
|---|
| 382 | putStrLn "Internal error while running expression:" |
|---|
| 383 | putStrLn $ show err |
|---|
| 384 | |
|---|
| 385 | runImperatively :: TVar Env -> Eval Val -> IO Val |
|---|
| 386 | runImperatively menv eval = do |
|---|
| 387 | env <- stm $ readTVar menv |
|---|
| 388 | runEvalIO env $ do |
|---|
| 389 | val <- eval |
|---|
| 390 | newEnv <- ask |
|---|
| 391 | stm $ writeTVar menv newEnv |
|---|
| 392 | return val |
|---|
| 393 | |
|---|
| 394 | doRun :: (?debugInfo :: DebugInfo) => String -> [String] -> String -> IO () |
|---|
| 395 | doRun = do |
|---|
| 396 | runProgramWith (\e -> e{ envDebug = ?debugInfo }) end |
|---|
| 397 | where |
|---|
| 398 | end err@(VError _ _) = do |
|---|
| 399 | hPutStrLn stderr $ encodeUTF8 $ pretty err |
|---|
| 400 | globalFinalize |
|---|
| 401 | exitFailure |
|---|
| 402 | end (VControl (ControlExit exit)) = do |
|---|
| 403 | globalFinalize |
|---|
| 404 | exitWith exit |
|---|
| 405 | end _ = return () |
|---|
| 406 | |
|---|
| 407 | noEnvDebug :: Env -> Env |
|---|
| 408 | noEnvDebug e = e{ envDebug = Nothing } |
|---|
| 409 | |
|---|
| 410 | runProgramWith :: |
|---|
| 411 | (Env -> Env) -> (Val -> IO a) -> VStr -> [VStr] -> String -> IO a |
|---|
| 412 | runProgramWith fenv f name args prog = do |
|---|
| 413 | env <- prepareEnv name args |
|---|
| 414 | -- Cache the compilation tree right here. |
|---|
| 415 | -- We only really care about envGlobal and envBody here. |
|---|
| 416 | val <- runEnv $ parseProgram (fenv env) name prog |
|---|
| 417 | f val |
|---|
| 418 | |
|---|
| 419 | createConfigLine :: String -> String |
|---|
| 420 | createConfigLine item = "\t" ++ item ++ ": " ++ (Map.findWithDefault "UNKNOWN" item config) |
|---|
| 421 | |
|---|
| 422 | printConfigInfo :: [String] -> IO () |
|---|
| 423 | printConfigInfo [] = do |
|---|
| 424 | libs <- getLibs |
|---|
| 425 | putStrLn $ unlines $ |
|---|
| 426 | ["This is " ++ version ++ " built for " ++ getConfig "archname" |
|---|
| 427 | ,"" |
|---|
| 428 | ,"Summary of pugs configuration:" ] |
|---|
| 429 | ++ map (\x -> createConfigLine x) (map (fst) (Map.toList config)) |
|---|
| 430 | ++ [ "" ] |
|---|
| 431 | ++ [ "@*INC:" ] ++ libs |
|---|
| 432 | |
|---|
| 433 | printConfigInfo (item:_) = do |
|---|
| 434 | putStrLn $ createConfigLine item |
|---|
| 435 | |
|---|
| 436 | compPIR :: String -> IO () |
|---|
| 437 | compPIR prog = do |
|---|
| 438 | pir <- doCompile "PIR" "-" prog |
|---|
| 439 | putStr $ (subMain ++ (last $ split subMain pir)) |
|---|
| 440 | where |
|---|
| 441 | subMain = ".sub main" |
|---|
| 442 | |
|---|
| 443 | runPIR :: String -> IO () |
|---|
| 444 | runPIR prog = do |
|---|
| 445 | pir <- doCompile "PIR" "-" prog |
|---|
| 446 | writeFile "a.pir" pir |
|---|
| 447 | fail "evalParrotFile is bitrotten." |
|---|
| 448 | -- evalParrotFile "a.pir" |
|---|
| 449 | |
|---|
| 450 | {- |
|---|
| 451 | withInlinedIncludes :: String -> IO String |
|---|
| 452 | withInlinedIncludes prog = do |
|---|
| 453 | libs <- getLibs |
|---|
| 454 | expandInc libs prog |
|---|
| 455 | where |
|---|
| 456 | expandInc :: [FilePath] -> String -> IO String |
|---|
| 457 | expandInc incs str = case breakOnGlue "\nuse " ('\n':str) of |
|---|
| 458 | Nothing -> case breakOnGlue "\nrequire " ('\n':str) of |
|---|
| 459 | Nothing -> return str |
|---|
| 460 | Just (pre, post) -> do |
|---|
| 461 | let (mod, (_:rest)) = span (/= ';') (dropWhile isSpace post) |
|---|
| 462 | mod' <- includeInc incs mod |
|---|
| 463 | rest' <- expandInc incs rest |
|---|
| 464 | return $ pre ++ mod' ++ rest' |
|---|
| 465 | Just (pre, post) -> do |
|---|
| 466 | let (mod, (_:rest)) = span isAlphaNum (dropWhile isSpace post) |
|---|
| 467 | mod' <- includeInc incs mod |
|---|
| 468 | rest' <- expandInc incs rest |
|---|
| 469 | return $ pre ++ "\n{" ++ mod' ++ "\n}\n" ++ rest' |
|---|
| 470 | includeInc :: [FilePath] -> String -> IO String |
|---|
| 471 | includeInc _ ('v':_) = return [] |
|---|
| 472 | includeInc incs name = do |
|---|
| 473 | let name' = concat (intersperse "/" names) ++ ".pm" |
|---|
| 474 | names = split "::" name |
|---|
| 475 | pathName <- requireInc incs name' (errMsg name incs) |
|---|
| 476 | readFile pathName |
|---|
| 477 | errMsg fn incs = "Can't locate " ++ fn ++ " in @*INC (@*INC contains: " ++ unwords incs ++ ")." |
|---|
| 478 | -} |
|---|