root/src/Pugs/Prim/Eval.hs

Revision 21673, 7.6 kB (checked in by audreyt, 2 months ago)

* Import Pugs 6.2.13.11 from Hackage into our source tree.
* Highlights:

  • Much faster startup time
  • Slightly faster compilation time (mostly due to refactored Pugs.AST.Internals)
  • Portable-to-Win32 readline thanks to Haskeline
  • Property svn:mime-type set to text/plain; charset=UTF-8
  • Property svn:eol-style set to native
Line 
1{-# OPTIONS_GHC -fglasgow-exts -fallow-overlapping-instances #-}
2module 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
11import Pugs.AST
12import Pugs.Parser.Program
13import Pugs.Embed
14import Pugs.Monads
15import Pugs.Internals
16import Pugs.Pretty
17import Pugs.Config
18import Pugs.Prim.Keyed
19import Pugs.Types
20import Pugs.Prelude
21import DrIFT.YAML
22import Data.Yaml.Syck
23import Data.Binary (decode)
24import qualified Data.ByteString.Char8 as Bytes
25
26type Bytes        = Bytes.ByteString
27
28data EvalError = EvalErrorFatal
29               | EvalErrorUndef
30               deriving Eq
31data EvalResult = EvalResultLastValue
32                | EvalResultModule
33                | EvalResultEnv
34                deriving Eq
35data EvalStyle = MkEvalStyle
36               { evalError  :: EvalError
37               , evalResult :: EvalResult
38               }
39
40
41specialPackageNames :: [String]
42specialPackageNames = ["MY", "OUR", "GLOBAL", "OUTER", "CALLER", "ENV", "SUPER", "COMPILING"]
43
44opRequire :: Bool -> Val -> Eval Val
45opRequire 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
116requireInc :: (MonadIO m) => [FilePath] -> FilePath -> String -> m String
117requireInc [] _ msg = fail msg
118requireInc (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
125opEvalFile :: String -> Eval Val
126opEvalFile 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
137op1EvalHaskell :: Val -> Eval Val
138op1EvalHaskell 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
147op1EvalP6Y, op1EvalFileP6Y :: Val -> Eval Val
148
149op1EvalFileP6Y fileName = do
150    fileName' <- fromVal fileName
151    file      <- io $ Bytes.readFile fileName'
152    op1EvalP6Y' file
153
154op1EvalP6Y bytecode = do
155    bytecode' <- fromVal bytecode
156    op1EvalP6Y' $ Bytes.pack bytecode' -- XXX: is this the right pack function?
157
158op1EvalP6Y' :: Bytes -> Eval Val
159op1EvalP6Y' 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
183opEval :: EvalStyle -> FilePath -> String -> Eval Val
184opEval 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
199retEvalResult :: EvalStyle -> Val -> Eval Val
200retEvalResult 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
Note: See TracBrowser for help on using the browser.