| 1 | {-# OPTIONS_GHC -fglasgow-exts -cpp -optc-w #-} |
|---|
| 2 | |
|---|
| 3 | #ifndef PUGS_HAVE_PERL5 |
|---|
| 4 | module Pugs.Embed.Perl5 |
|---|
| 5 | ( InvokePerl5Result(..) |
|---|
| 6 | , svToVBool, svToVInt, svToVNum, svToVStr, vstrToSV, vintToSV, svToVal, bufToSV, svUndef |
|---|
| 7 | , vnumToSV, mkValRef, mkValPtr, mkEnv, PerlSV, nullSV, nullEnv, evalPerl5, invokePerl5 |
|---|
| 8 | , initPerl5, freePerl5, canPerl5 |
|---|
| 9 | , evalPCR, pugs_SvToVal |
|---|
| 10 | ) |
|---|
| 11 | where |
|---|
| 12 | import Foreign.C.Types |
|---|
| 13 | import System.Directory |
|---|
| 14 | import Pugs.Internals |
|---|
| 15 | import qualified Data.ByteString.UTF8 as Str |
|---|
| 16 | |
|---|
| 17 | evalPCR :: [FilePath] -> String -> String -> [(String, String)] -> IO String |
|---|
| 18 | evalPCR [path] match rule subrules = do |
|---|
| 19 | (inp, out, err, pid) <- initPCR $ head path |
|---|
| 20 | (`mapM` subrules) $ \(name, rule) -> do |
|---|
| 21 | let nameStr = escape name |
|---|
| 22 | ruleStr = escape rule |
|---|
| 23 | hPutStrLn inp $ unwords |
|---|
| 24 | ["add_rule", show (length nameStr), show (length ruleStr)] |
|---|
| 25 | hPutStrLn inp nameStr |
|---|
| 26 | hPutStrLn inp ruleStr |
|---|
| 27 | let matchStr = escape match |
|---|
| 28 | ruleStr = escape rule |
|---|
| 29 | hPutStrLn inp $ unwords |
|---|
| 30 | ["match", show (length matchStr), show (length ruleStr)] |
|---|
| 31 | hPutStrLn inp $ matchStr |
|---|
| 32 | hPutStrLn inp $ ruleStr |
|---|
| 33 | hFlush inp |
|---|
| 34 | rv <- hGetLine out |
|---|
| 35 | case rv of |
|---|
| 36 | ('O':'K':' ':_) -> do |
|---|
| 37 | -- size <- readIO sizeStr |
|---|
| 38 | -- rv <- sequence (replicate size (hGetChar out)) |
|---|
| 39 | ln <- hGetLine out |
|---|
| 40 | ln2 <- hGetLine out |
|---|
| 41 | return $ ln ++ ln2 |
|---|
| 42 | _ -> do |
|---|
| 43 | errMsg <- fmap (rv ++) (hGetContents err) |
|---|
| 44 | rc <- waitForProcess pid |
|---|
| 45 | writeIORef _Perl5Interp Nothing |
|---|
| 46 | let msg | null errMsg = show rc |
|---|
| 47 | | otherwise = errMsg |
|---|
| 48 | fail $ "*** Running external 'perl' failed:\n" ++ msg |
|---|
| 49 | where |
|---|
| 50 | escape "" = "" |
|---|
| 51 | escape ('\\':xs) = "\\\\" ++ escape xs |
|---|
| 52 | escape ('\n':xs) = "\\n" ++ escape xs |
|---|
| 53 | escape (x:xs) = (x:escape xs) |
|---|
| 54 | |
|---|
| 55 | initPCR :: FilePath -> IO Perl5Interp |
|---|
| 56 | initPCR path = do |
|---|
| 57 | rv <- readIORef _Perl5Interp |
|---|
| 58 | case rv of |
|---|
| 59 | Just interp@(_, _, _, pid) -> do |
|---|
| 60 | gone <- getProcessExitCode pid |
|---|
| 61 | if isNothing gone then return interp else do |
|---|
| 62 | writeIORef _Perl5Interp Nothing |
|---|
| 63 | initPCR path |
|---|
| 64 | Nothing -> do |
|---|
| 65 | cmd <- findPerl5 |
|---|
| 66 | interp <- runInteractiveProcess cmd |
|---|
| 67 | [ "-Iperl5/Pugs-Compiler-Rule/lib" |
|---|
| 68 | , "-MPugs::Runtime::Match::HsBridge" |
|---|
| 69 | , "-ePugs::Runtime::Match::HsBridge::__CMD__" |
|---|
| 70 | ] (Just path) Nothing |
|---|
| 71 | writeIORef _Perl5Interp (Just interp) |
|---|
| 72 | return interp |
|---|
| 73 | where |
|---|
| 74 | findPerl5 :: IO FilePath |
|---|
| 75 | findPerl5 = do |
|---|
| 76 | rv <- findExecutable "perl" |
|---|
| 77 | case rv of |
|---|
| 78 | Nothing -> do |
|---|
| 79 | rv' <- findExecutable "perl.exe" |
|---|
| 80 | case rv' of |
|---|
| 81 | Just cmd -> return cmd |
|---|
| 82 | Nothing -> fail "Cannot find the parrot executable in PATH" |
|---|
| 83 | Just cmd -> return cmd |
|---|
| 84 | |
|---|
| 85 | type Perl5Interp = (Handle, Handle, Handle, ProcessHandle) |
|---|
| 86 | |
|---|
| 87 | {-# NOINLINE _Perl5Interp #-} |
|---|
| 88 | _Perl5Interp :: IORef (Maybe Perl5Interp) |
|---|
| 89 | _Perl5Interp = unsafePerformIO $ newIORef Nothing |
|---|
| 90 | |
|---|
| 91 | type PerlInterpreter = () |
|---|
| 92 | data PerlSV = MkPerlSV -- phantom type |
|---|
| 93 | deriving (Show, Eq, Ord, Typeable) |
|---|
| 94 | type PugsVal = PerlSV |
|---|
| 95 | type PugsEnv = PerlSV |
|---|
| 96 | |
|---|
| 97 | data InvokePerl5Result |
|---|
| 98 | = Perl5ReturnValues [PerlSV] |
|---|
| 99 | | Perl5ErrorString String |
|---|
| 100 | | Perl5ErrorObject PerlSV |
|---|
| 101 | deriving (Show, Typeable) |
|---|
| 102 | |
|---|
| 103 | constFail :: a -> IO b |
|---|
| 104 | constFail = const $ fail "perl5 not embedded" |
|---|
| 105 | |
|---|
| 106 | initPerl5 :: String -> Maybe a -> IO PerlInterpreter |
|---|
| 107 | initPerl5 _ _ = return () |
|---|
| 108 | |
|---|
| 109 | freePerl5 :: PerlInterpreter -> IO () |
|---|
| 110 | freePerl5 _ = return () |
|---|
| 111 | |
|---|
| 112 | evalPerl5 :: String -> PugsEnv -> CInt -> IO PerlSV |
|---|
| 113 | evalPerl5 _ _ = constFail |
|---|
| 114 | |
|---|
| 115 | svToVStr :: PerlSV -> IO String |
|---|
| 116 | svToVStr = constFail |
|---|
| 117 | |
|---|
| 118 | svToVInt :: (Num a) => PerlSV -> IO a |
|---|
| 119 | svToVInt = constFail |
|---|
| 120 | |
|---|
| 121 | svToVNum :: (Fractional a) => PerlSV -> IO a |
|---|
| 122 | svToVNum = constFail |
|---|
| 123 | |
|---|
| 124 | svToVBool :: PerlSV -> IO Bool |
|---|
| 125 | svToVBool = constFail |
|---|
| 126 | |
|---|
| 127 | svToVal :: PerlSV -> IO a |
|---|
| 128 | svToVal = constFail |
|---|
| 129 | |
|---|
| 130 | mkValPtr :: (Show a) => a -> IO PugsVal |
|---|
| 131 | mkValPtr = constFail |
|---|
| 132 | |
|---|
| 133 | mkEnv :: a -> IO PugsVal |
|---|
| 134 | mkEnv = constFail |
|---|
| 135 | |
|---|
| 136 | mkValRef :: a -> String -> IO PerlSV |
|---|
| 137 | mkValRef _ = constFail |
|---|
| 138 | |
|---|
| 139 | vstrToSV :: String -> IO PerlSV |
|---|
| 140 | vstrToSV = constFail |
|---|
| 141 | |
|---|
| 142 | svUndef :: IO PerlSV |
|---|
| 143 | svUndef = error "perl5 not embedded" |
|---|
| 144 | |
|---|
| 145 | bufToSV :: ByteString -> IO PerlSV |
|---|
| 146 | bufToSV = constFail |
|---|
| 147 | |
|---|
| 148 | vintToSV :: (Integral a) => a -> IO PerlSV |
|---|
| 149 | vintToSV = constFail |
|---|
| 150 | |
|---|
| 151 | vnumToSV :: (Real a) => a -> IO PerlSV |
|---|
| 152 | vnumToSV = constFail |
|---|
| 153 | |
|---|
| 154 | invokePerl5 :: PerlSV -> PerlSV -> [PerlSV] -> PugsEnv -> CInt -> IO InvokePerl5Result |
|---|
| 155 | invokePerl5 _ _ _ _ = constFail |
|---|
| 156 | |
|---|
| 157 | canPerl5 :: PerlSV -> ByteString -> IO Bool |
|---|
| 158 | canPerl5 MkPerlSV = constFail |
|---|
| 159 | |
|---|
| 160 | pugs_SvToVal :: PerlSV -> IO PugsVal |
|---|
| 161 | pugs_SvToVal = constFail |
|---|
| 162 | |
|---|
| 163 | nullSV :: PerlSV |
|---|
| 164 | nullSV = error "perl5 not embedded" |
|---|
| 165 | |
|---|
| 166 | nullEnv :: PugsVal |
|---|
| 167 | nullEnv = error "perl5 not embedded" |
|---|
| 168 | |
|---|
| 169 | -- Below are unused |
|---|
| 170 | |
|---|
| 171 | -- mkSV :: IO PerlSV -> IO PerlSV |
|---|
| 172 | -- mkSV = id |
|---|
| 173 | |
|---|
| 174 | -- perl5_SvROK :: IO PerlSV -> IO Bool |
|---|
| 175 | -- perl5_SvROK _ = return False |
|---|
| 176 | |
|---|
| 177 | #else |
|---|
| 178 | #undef RETURN |
|---|
| 179 | |
|---|
| 180 | {-# INCLUDE "../../perl5/p5embed.h" #-} |
|---|
| 181 | {-# INCLUDE "../../perl5/pugsembed.h" #-} |
|---|
| 182 | |
|---|
| 183 | module Pugs.Embed.Perl5 where |
|---|
| 184 | import Pugs.Internals |
|---|
| 185 | import Foreign |
|---|
| 186 | import Foreign.C.Types |
|---|
| 187 | import Foreign.C.String |
|---|
| 188 | import {-# SOURCE #-} Pugs.AST.Internals |
|---|
| 189 | import qualified Data.ByteString.UTF8 as Str |
|---|
| 190 | import qualified Data.ByteString.Char8 as Buf |
|---|
| 191 | import qualified Pugs.Val as Val |
|---|
| 192 | |
|---|
| 193 | type PerlInterpreter = Ptr () |
|---|
| 194 | type PerlSV = Ptr () |
|---|
| 195 | type PugsVal = StablePtr Val |
|---|
| 196 | type PugsEnv = StablePtr Env |
|---|
| 197 | |
|---|
| 198 | foreign import ccall "perl_alloc" |
|---|
| 199 | perl_alloc :: IO PerlInterpreter |
|---|
| 200 | foreign import ccall "perl_construct" |
|---|
| 201 | perl_construct :: PerlInterpreter -> IO () |
|---|
| 202 | foreign import ccall "perl_run" |
|---|
| 203 | perl_run :: PerlInterpreter -> IO CInt |
|---|
| 204 | foreign import ccall "perl_destruct" |
|---|
| 205 | perl_destruct :: PerlInterpreter -> IO CInt |
|---|
| 206 | foreign import ccall "perl_free" |
|---|
| 207 | perl_free :: PerlInterpreter -> IO () |
|---|
| 208 | {- |
|---|
| 209 | foreign import ccall "boot_DynaLoader" |
|---|
| 210 | boot_DynaLoader :: Ptr () -> IO () |
|---|
| 211 | -} |
|---|
| 212 | foreign import ccall "perl5_finalize" |
|---|
| 213 | perl5_finalize :: PerlSV -> IO () |
|---|
| 214 | foreign import ccall "perl5_SvPV" |
|---|
| 215 | perl5_SvPV :: PerlSV -> IO CString |
|---|
| 216 | foreign import ccall "perl5_SvIV" |
|---|
| 217 | perl5_SvIV :: PerlSV -> IO CInt |
|---|
| 218 | foreign import ccall "perl5_SvNV" |
|---|
| 219 | perl5_SvNV :: PerlSV -> IO CDouble |
|---|
| 220 | foreign import ccall "perl5_SvTRUE" |
|---|
| 221 | perl5_SvTRUE :: PerlSV -> IO Bool |
|---|
| 222 | foreign import ccall "perl5_SvROK" |
|---|
| 223 | perl5_SvROK :: PerlSV -> IO Bool |
|---|
| 224 | foreign import ccall "perl5_newSVpvn" |
|---|
| 225 | perl5_newSVpvn :: CString -> CInt -> IO PerlSV |
|---|
| 226 | foreign import ccall "perl5_newSViv" |
|---|
| 227 | perl5_newSViv :: CInt -> IO PerlSV |
|---|
| 228 | foreign import ccall "perl5_newSVnv" |
|---|
| 229 | perl5_newSVnv :: CDouble -> IO PerlSV |
|---|
| 230 | foreign import ccall "perl5_sv_undef" |
|---|
| 231 | perl5_sv_undef :: IO PerlSV |
|---|
| 232 | foreign import ccall "perl5_get_sv" |
|---|
| 233 | perl5_get_sv :: CString -> IO PerlSV |
|---|
| 234 | foreign import ccall "perl5_apply" |
|---|
| 235 | perl5_apply :: PerlSV -> PerlSV -> Ptr PerlSV -> PugsEnv -> CInt -> IO (Ptr PerlSV) |
|---|
| 236 | foreign import ccall "perl5_can" |
|---|
| 237 | perl5_can :: PerlSV -> CString -> IO Bool |
|---|
| 238 | foreign import ccall "perl5_eval" |
|---|
| 239 | perl5_eval :: CString -> PugsEnv -> CInt -> IO PerlSV |
|---|
| 240 | foreign import ccall "perl5_init" |
|---|
| 241 | perl5_init :: CInt -> Ptr CString -> IO PerlInterpreter |
|---|
| 242 | |
|---|
| 243 | foreign import ccall "pugs_getenv" |
|---|
| 244 | pugs_getenv :: IO PugsEnv |
|---|
| 245 | foreign import ccall "pugs_setenv" |
|---|
| 246 | pugs_setenv :: PugsEnv -> IO () |
|---|
| 247 | |
|---|
| 248 | foreign import ccall "pugs_SvToVal" |
|---|
| 249 | pugs_SvToVal :: PerlSV -> IO PugsVal |
|---|
| 250 | foreign import ccall "pugs_MkValRef" |
|---|
| 251 | pugs_MkValRef :: PugsVal -> CString -> IO PerlSV |
|---|
| 252 | |
|---|
| 253 | initPerl5 :: String -> Maybe Env -> IO PerlInterpreter |
|---|
| 254 | initPerl5 str env = do |
|---|
| 255 | withCString "-e" $ \prog -> withCString str $ \cstr -> do |
|---|
| 256 | withArray [prog, prog, cstr] $ \argv -> do |
|---|
| 257 | interp <- perl5_init 3 argv |
|---|
| 258 | case env of |
|---|
| 259 | Just val -> pugs_setenv =<< mkEnv val |
|---|
| 260 | Nothing -> return () |
|---|
| 261 | modifyIORef _GlobalFinalizer (>> perl_free interp) |
|---|
| 262 | return interp |
|---|
| 263 | |
|---|
| 264 | mkValPtr :: Val -> IO PugsVal |
|---|
| 265 | mkValPtr x = do |
|---|
| 266 | -- warn "Creating nonblessed stable pointer for " (showVal x) |
|---|
| 267 | newStablePtr x |
|---|
| 268 | |
|---|
| 269 | mkEnv :: Env -> IO PugsEnv |
|---|
| 270 | mkEnv = newStablePtr |
|---|
| 271 | |
|---|
| 272 | svToVStr :: PerlSV -> IO String |
|---|
| 273 | svToVStr sv = peekCString =<< perl5_SvPV sv |
|---|
| 274 | |
|---|
| 275 | svToVInt :: (Num a) => PerlSV -> IO a |
|---|
| 276 | svToVInt sv = fmap fromIntegral $ perl5_SvIV sv |
|---|
| 277 | |
|---|
| 278 | svToVNum :: (Fractional a) => PerlSV -> IO a |
|---|
| 279 | svToVNum sv = fmap realToFrac $ perl5_SvNV sv |
|---|
| 280 | |
|---|
| 281 | svToVBool :: PerlSV -> IO Bool |
|---|
| 282 | svToVBool = perl5_SvTRUE |
|---|
| 283 | |
|---|
| 284 | svToVal :: PerlSV -> IO Val |
|---|
| 285 | svToVal sv = do |
|---|
| 286 | ptr <- pugs_SvToVal sv |
|---|
| 287 | deRefStablePtr ptr |
|---|
| 288 | |
|---|
| 289 | mkValRef :: Val -> String -> IO PerlSV |
|---|
| 290 | mkValRef x typ = do |
|---|
| 291 | -- warn "Creating stable pointer for " (showVal x) |
|---|
| 292 | val <- mkValPtr x |
|---|
| 293 | withCString typ (pugs_MkValRef val) |
|---|
| 294 | |
|---|
| 295 | svUndef :: IO PerlSV |
|---|
| 296 | svUndef = perl5_sv_undef |
|---|
| 297 | |
|---|
| 298 | vstrToSV :: String -> IO PerlSV |
|---|
| 299 | vstrToSV str = Buf.useAsCStringLen (cast str) $ \(cstr, len) -> perl5_newSVpvn cstr (toEnum len) |
|---|
| 300 | |
|---|
| 301 | bufToSV :: ByteString -> IO PerlSV |
|---|
| 302 | bufToSV str = Buf.useAsCStringLen str $ \(cstr, len) -> perl5_newSVpvn cstr (toEnum len) |
|---|
| 303 | |
|---|
| 304 | vintToSV :: (Integral a) => a -> IO PerlSV |
|---|
| 305 | vintToSV int = perl5_newSViv (fromIntegral int) |
|---|
| 306 | |
|---|
| 307 | vnumToSV :: (Real a) => a -> IO PerlSV |
|---|
| 308 | vnumToSV int = perl5_newSVnv (realToFrac int) |
|---|
| 309 | |
|---|
| 310 | |
|---|
| 311 | data InvokePerl5Result |
|---|
| 312 | = Perl5ReturnValues [PerlSV] |
|---|
| 313 | | Perl5ErrorString String |
|---|
| 314 | | Perl5ErrorObject PerlSV |
|---|
| 315 | deriving (Show, Typeable) |
|---|
| 316 | |
|---|
| 317 | invokePerl5 :: PerlSV -> PerlSV -> [PerlSV] -> PugsEnv -> CInt -> IO InvokePerl5Result |
|---|
| 318 | invokePerl5 sub inv args env cxt = do |
|---|
| 319 | withArray0 nullPtr args $ \argv -> do |
|---|
| 320 | rv <- perl5_apply sub inv argv env cxt |
|---|
| 321 | svs <- peekArray0 nullPtr rv |
|---|
| 322 | |
|---|
| 323 | -- If it's empty, no error occured (see p5embed.c on out[0]). |
|---|
| 324 | -- Otherwise, the second slot may be a stringified version we should use. |
|---|
| 325 | case svs of |
|---|
| 326 | [] -> fmap Perl5ReturnValues $ peekArray0 nullPtr (rv `advancePtr` 1) |
|---|
| 327 | [err] -> return $ Perl5ErrorObject err |
|---|
| 328 | (_:x:_) -> do |
|---|
| 329 | str <- svToVStr x |
|---|
| 330 | return $ Perl5ErrorString str |
|---|
| 331 | |
|---|
| 332 | canPerl5 :: PerlSV -> ByteString -> IO Bool |
|---|
| 333 | canPerl5 sv meth = Buf.useAsCString meth $ \cstr -> perl5_can sv cstr |
|---|
| 334 | |
|---|
| 335 | mkSV :: IO PerlSV -> IO PerlSV |
|---|
| 336 | mkSV action = action |
|---|
| 337 | {- |
|---|
| 338 | - do |
|---|
| 339 | sv <- action |
|---|
| 340 | addFinalizer sv (perl5_finalize sv) |
|---|
| 341 | return sv |
|---|
| 342 | -} |
|---|
| 343 | |
|---|
| 344 | evalPerl5 :: String -> PugsEnv -> CInt -> IO PerlSV |
|---|
| 345 | evalPerl5 str env cxt = mkSV $ Buf.useAsCString (cast str) $ \cstr -> perl5_eval cstr env cxt |
|---|
| 346 | |
|---|
| 347 | freePerl5 :: PerlInterpreter -> IO () |
|---|
| 348 | freePerl5 my_perl = do |
|---|
| 349 | perl_destruct my_perl |
|---|
| 350 | return () |
|---|
| 351 | |
|---|
| 352 | nullSV :: PerlSV |
|---|
| 353 | nullSV = nullPtr |
|---|
| 354 | |
|---|
| 355 | {-# NOINLINE nullEnv #-} |
|---|
| 356 | nullEnv :: PugsEnv |
|---|
| 357 | nullEnv = unsafePerformIO (newStablePtr (error "undefined environment")) |
|---|
| 358 | |
|---|
| 359 | evalPCR :: [FilePath] -> String -> String -> [(String, String)] -> IO String |
|---|
| 360 | evalPCR paths match rule subrules = do |
|---|
| 361 | let bridgeMod = "Pugs::Runtime::Match::HsBridge" |
|---|
| 362 | bridgeFile = "Pugs/Runtime/Match/HsBridge.pm"; |
|---|
| 363 | incs = map (\p -> " unshift @INC, '"++p++"';") paths |
|---|
| 364 | inv <- evalPerl5 (unlines $ |
|---|
| 365 | [ "if (!$INC{'"++bridgeFile++"'}) {" |
|---|
| 366 | ] ++ incs ++ |
|---|
| 367 | [ " eval q[require '"++bridgeFile++"'] or die $@;" |
|---|
| 368 | , "}" |
|---|
| 369 | , "'"++bridgeMod++"'" |
|---|
| 370 | ]) nullEnv 1 |
|---|
| 371 | meth <- vstrToSV "__RUN__" |
|---|
| 372 | args <- mapM vstrToSV $ concatMap (\(x, y) -> [x, y]) ((match, rule):subrules) |
|---|
| 373 | rv <- invokePerl5 meth inv args nullEnv 1 |
|---|
| 374 | case rv of |
|---|
| 375 | Perl5ReturnValues [] -> return "" |
|---|
| 376 | Perl5ReturnValues (x:_) -> svToVStr x |
|---|
| 377 | Perl5ErrorString err -> return $ "Error: " ++ err |
|---|
| 378 | Perl5ErrorObject obj -> do |
|---|
| 379 | err <- svToVStr obj |
|---|
| 380 | return $ "Error: " ++ err |
|---|
| 381 | |
|---|
| 382 | #endif |
|---|