| 1 | {-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances #-} |
|---|
| 2 | module Pugs.Prim.Eval ( |
|---|
| 3 | -- used by Pugs.Prim |
|---|
| 4 | op1EvalHaskell, op1EvalP6Y, op1EvalFileP6Y, |
|---|
| 5 | opEval, opEvalFile, |
|---|
| 6 | opRequire, requireInc, |
|---|
| 7 | EvalError(..), EvalResult(..), EvalStyle(..), |
|---|
| 8 | -- used by Pugs.Eval -- needs factored somewhere bettwen |
|---|
| 9 | retEvalResult, |
|---|
| 10 | ) where |
|---|
| 11 | import Pugs.AST |
|---|
| 12 | import Pugs.Parser.Program |
|---|
| 13 | import Pugs.Embed |
|---|
| 14 | import Pugs.Monads |
|---|
| 15 | import Pugs.Internals |
|---|
| 16 | import Pugs.Pretty |
|---|
| 17 | import Pugs.Config |
|---|
| 18 | import Pugs.Prim.Keyed |
|---|
| 19 | import Pugs.Types |
|---|
| 20 | import Pugs.Prelude |
|---|
| 21 | import DrIFT.YAML |
|---|
| 22 | import Data.Yaml.Syck |
|---|
| 23 | import Data.Binary (decode) |
|---|
| 24 | import qualified Data.ByteString.Char8 as Bytes |
|---|
| 25 | |
|---|
| 26 | type Bytes = Bytes.ByteString |
|---|
| 27 | |
|---|
| 28 | data EvalError = EvalErrorFatal |
|---|
| 29 | | EvalErrorUndef |
|---|
| 30 | deriving Eq |
|---|
| 31 | data EvalResult = EvalResultLastValue |
|---|
| 32 | | EvalResultModule |
|---|
| 33 | | EvalResultEnv |
|---|
| 34 | deriving Eq |
|---|
| 35 | data EvalStyle = MkEvalStyle |
|---|
| 36 | { evalError :: EvalError |
|---|
| 37 | , evalResult :: EvalResult |
|---|
| 38 | } |
|---|
| 39 | |
|---|
| 40 | |
|---|
| 41 | specialPackageNames :: [String] |
|---|
| 42 | specialPackageNames = ["MY", "OUR", "GLOBAL", "OUTER", "CALLER", "ENV", "SUPER", "COMPILING"] |
|---|
| 43 | |
|---|
| 44 | opRequire :: Bool -> Val -> Eval Val |
|---|
| 45 | opRequire dumpEnv v = do |
|---|
| 46 | mod <- fromVal v |
|---|
| 47 | if elem mod specialPackageNames then return (VBool True) else do |
|---|
| 48 | incs <- fromVal =<< readVar (cast "@*INC") |
|---|
| 49 | glob <- askGlobal |
|---|
| 50 | seen <- findSymRef (cast "%*INC") glob |
|---|
| 51 | loaded <- existsFromRef seen v |
|---|
| 52 | let file | '.' `elem` mod = mod |
|---|
| 53 | | otherwise = (concat $ intersperse (getConfig "file_sep") $ split "::" mod) ++ ".pm" |
|---|
| 54 | pathName <- case mod of |
|---|
| 55 | "Test" -> return "Test.pm" |
|---|
| 56 | _ -> requireInc incs file (errMsg file incs) |
|---|
| 57 | if loaded then opEval style pathName "" else do |
|---|
| 58 | -- %*INC{mod} = { relname => file, pathname => pathName } |
|---|
| 59 | evalExp $ Syn "=" |
|---|
| 60 | [ Syn "{}" -- subscript |
|---|
| 61 | [ _Var "%*INC", Val . VStr $ decodeUTF8 mod ] |
|---|
| 62 | , Syn "\\{}" -- hashref |
|---|
| 63 | [ Syn "," [ mkStrPair "fullpath" (decodeUTF8 pathName) |
|---|
| 64 | , mkStrPair "relpath" (decodeUTF8 file) ] |
|---|
| 65 | ] |
|---|
| 66 | ] |
|---|
| 67 | -- merge @*END here |
|---|
| 68 | endAV <- findSymRef (cast "@*END") glob |
|---|
| 69 | ends <- fromVal =<< readRef endAV |
|---|
| 70 | clearRef endAV |
|---|
| 71 | rv <- case mod of |
|---|
| 72 | "Test" -> shortcutToTestPM |
|---|
| 73 | _ -> tryFastEval pathName (pathName ++ ".yml") |
|---|
| 74 | endAV' <- findSymRef (cast "@*END") glob |
|---|
| 75 | doArray (VRef endAV') (`array_unshift` ends) |
|---|
| 76 | return rv |
|---|
| 77 | where |
|---|
| 78 | shortcutToTestPM = do |
|---|
| 79 | globTVar <- asks envGlobal |
|---|
| 80 | let MkCompUnit _ _ glob ast = decode (testByteStringLazy) |
|---|
| 81 | -- Inject the global bindings |
|---|
| 82 | stm $ do |
|---|
| 83 | glob' <- readMPad globTVar |
|---|
| 84 | writeMPad globTVar (glob `unionPads` glob') |
|---|
| 85 | |
|---|
| 86 | -- | PEStatic { pe_type :: !Type, pe_proto :: !VRef, pe_flags :: !EntryFlags, pe_store :: !(TVar VRef) } |
|---|
| 87 | evl <- asks envEval |
|---|
| 88 | evl ast |
|---|
| 89 | tryFastEval pathName pathNameYml = do |
|---|
| 90 | io $ print pathNameYml |
|---|
| 91 | ok <- io $ doesFileExist pathNameYml |
|---|
| 92 | if not ok then slowEval pathName else do |
|---|
| 93 | isYamlStale <- tryIO False $ do |
|---|
| 94 | timePm <- getModificationTime pathName |
|---|
| 95 | timeYml <- getModificationTime pathNameYml |
|---|
| 96 | return (timeYml < timePm) |
|---|
| 97 | if isYamlStale then slowEval pathName else do |
|---|
| 98 | rv <- tryT $ fastEval pathNameYml |
|---|
| 99 | case rv of |
|---|
| 100 | VError _ [MkPos{posBeginLine=0}]-> slowEval pathName |
|---|
| 101 | _ -> opEval style pathName "" |
|---|
| 102 | |
|---|
| 103 | |
|---|
| 104 | fastEval = op1EvalFileP6Y . VStr |
|---|
| 105 | slowEval pathName = do |
|---|
| 106 | str <- io $ readFile pathName |
|---|
| 107 | opEval style pathName str |
|---|
| 108 | style = MkEvalStyle |
|---|
| 109 | { evalError = EvalErrorFatal |
|---|
| 110 | , evalResult = (if dumpEnv == True then EvalResultEnv |
|---|
| 111 | else EvalResultLastValue)} |
|---|
| 112 | errMsg file incs = "Can't locate " ++ file ++ " in @*INC (@*INC contains: " ++ unwords incs ++ ")." |
|---|
| 113 | mkStrPair :: String -> String -> Exp |
|---|
| 114 | mkStrPair key val = App (_Var "&infix:=>") Nothing (map (Val . VStr) [key, val]) |
|---|
| 115 | |
|---|
| 116 | requireInc :: (MonadIO m) => [FilePath] -> FilePath -> String -> m String |
|---|
| 117 | requireInc [] _ msg = fail msg |
|---|
| 118 | requireInc (p:ps) file msg = do |
|---|
| 119 | let pathName = p ++ (getConfig "file_sep") ++ file |
|---|
| 120 | ok <- io $ doesFileExist pathName |
|---|
| 121 | if (not ok) |
|---|
| 122 | then requireInc ps file msg |
|---|
| 123 | else return pathName |
|---|
| 124 | |
|---|
| 125 | opEvalFile :: String -> Eval Val |
|---|
| 126 | opEvalFile filename = do |
|---|
| 127 | ok <- io $ doesFileExist filename |
|---|
| 128 | if (not ok) |
|---|
| 129 | then fail $ "Can't locate " ++ filename ++ "." |
|---|
| 130 | else do |
|---|
| 131 | contents <- io $ readFile filename |
|---|
| 132 | opEval style filename contents |
|---|
| 133 | where |
|---|
| 134 | style = MkEvalStyle{ evalError=EvalErrorUndef |
|---|
| 135 | , evalResult=EvalResultLastValue} |
|---|
| 136 | |
|---|
| 137 | op1EvalHaskell :: Val -> Eval Val |
|---|
| 138 | op1EvalHaskell cv = do |
|---|
| 139 | str <- fromVal cv |
|---|
| 140 | val <- tryT $ evalHaskell str |
|---|
| 141 | retEvalResult style val |
|---|
| 142 | where |
|---|
| 143 | style = MkEvalStyle{ evalError=EvalErrorUndef |
|---|
| 144 | , evalResult=EvalResultLastValue} |
|---|
| 145 | |
|---|
| 146 | |
|---|
| 147 | op1EvalP6Y, op1EvalFileP6Y :: Val -> Eval Val |
|---|
| 148 | |
|---|
| 149 | op1EvalFileP6Y fileName = do |
|---|
| 150 | fileName' <- fromVal fileName |
|---|
| 151 | file <- io $ Bytes.readFile fileName' |
|---|
| 152 | op1EvalP6Y' file |
|---|
| 153 | |
|---|
| 154 | op1EvalP6Y bytecode = do |
|---|
| 155 | bytecode' <- fromVal bytecode |
|---|
| 156 | op1EvalP6Y' $ Bytes.pack bytecode' -- XXX: is this the right pack function? |
|---|
| 157 | |
|---|
| 158 | op1EvalP6Y' :: Bytes -> Eval Val |
|---|
| 159 | op1EvalP6Y' bytecode = do |
|---|
| 160 | yml <- io $ (`catchIO` (return . Left . show)) $ |
|---|
| 161 | fmap Right (parseYamlBytes bytecode) |
|---|
| 162 | case yml of |
|---|
| 163 | Right MkNode{ n_elem=ESeq (v:_) } |
|---|
| 164 | | MkNode{ n_elem=EStr vnum } <- v |
|---|
| 165 | , vnum /= (packBuf $ show compUnitVersion) -> do |
|---|
| 166 | err $ "incompatible version number for compilation unit: found " ++ |
|---|
| 167 | unpackBuf vnum ++ ", expecting " ++ (show compUnitVersion) |
|---|
| 168 | Right yml' -> do |
|---|
| 169 | globTVar <- asks envGlobal |
|---|
| 170 | MkCompUnit _ _ glob ast <- io $ fromYAML yml' |
|---|
| 171 | tryT $ do |
|---|
| 172 | -- Inject the global bindings |
|---|
| 173 | stm $ do |
|---|
| 174 | glob' <- readMPad globTVar |
|---|
| 175 | writeMPad globTVar (glob `unionPads` glob') |
|---|
| 176 | evl <- asks envEval |
|---|
| 177 | evl ast |
|---|
| 178 | x -> err x |
|---|
| 179 | where |
|---|
| 180 | err x = local (\e -> e{ envPos = (envPos e){ posBeginLine=0 } }) $ |
|---|
| 181 | fail $ "failed loading Yaml: " ++ show x |
|---|
| 182 | |
|---|
| 183 | opEval :: EvalStyle -> FilePath -> String -> Eval Val |
|---|
| 184 | opEval style path str = enterCaller $ do |
|---|
| 185 | env <- ask |
|---|
| 186 | let errHandler err = return env{ envBody = Val $ VError (VStr (show err)) [] } |
|---|
| 187 | env' <- io $ evaluateIO (parseProgram env path str) `catchIO` errHandler |
|---|
| 188 | val <- tryT $ local (const env') $ do |
|---|
| 189 | evl <- asks envEval |
|---|
| 190 | initAV <- evalExp (_Var "@*INIT") |
|---|
| 191 | initSubs <- fromVals initAV |
|---|
| 192 | mapM_ evalExp [ Ann (Cxt CxtVoid) (App (Val sub) Nothing []) | sub@VCode{} <- initSubs ] |
|---|
| 193 | evalExp (Syn "=" [_Var "@*INIT", Syn "," []]) |
|---|
| 194 | evl $ case evalResult style of |
|---|
| 195 | EvalResultEnv -> envBody env' `mergeStmts` Syn "continuation" [] |
|---|
| 196 | _ -> envBody env' |
|---|
| 197 | retEvalResult style val |
|---|
| 198 | |
|---|
| 199 | retEvalResult :: EvalStyle -> Val -> Eval Val |
|---|
| 200 | retEvalResult style val = do |
|---|
| 201 | glob <- askGlobal |
|---|
| 202 | errSV <- findSymRef (cast "$!") glob |
|---|
| 203 | case val of |
|---|
| 204 | err@(VError e _) -> do |
|---|
| 205 | writeRef errSV e |
|---|
| 206 | when (evalError style == EvalErrorFatal) $ do |
|---|
| 207 | io $ fail $ pretty err |
|---|
| 208 | retEmpty |
|---|
| 209 | _ -> do |
|---|
| 210 | writeRef errSV VUndef |
|---|
| 211 | return val |
|---|