--
-- Copyright (C) 2004-5 Don Stewart - http://www.cse.unsw.edu.au/~dons
--
-- This library is free software; you can redistribute it and/or
-- modify it under the terms of the GNU Lesser General Public
-- License as published by the Free Software Foundation; either
-- version 2.1 of the License, or (at your option) any later version.
--
-- This library is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-- Lesser General Public License for more details.
--
-- You should have received a copy of the GNU Lesser General Public
-- License along with this library; if not, write to the Free Software
-- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
-- USA
--

--
-- | Evaluate Haskell at runtime, using runtime compilation and dynamic
-- loading. Arguments are compiled to native code, and dynamically
-- loaded, returning a Haskell value representing the compiled argument.
-- The underlying implementation treats 'String' arguments as the source
-- for plugins to be compiled at runtime.
--

module System.Eval.Haskell (
        eval,
        eval_,
        unsafeEval,
        unsafeEval_,
        typeOf,
        mkHsValues,

{-
        hs_eval_b,      -- return a Bool
        hs_eval_c,      -- return a CChar
        hs_eval_i,      -- return a CInt
        hs_eval_s,      -- return a CString
-}

        module System.Eval.Utils,

    ) where

import System.Eval.Utils
import System.Plugins.Make
import System.Plugins.Load

import Data.Dynamic             ( Dynamic )
import Data.Typeable            ( Typeable )

import Data.Either              ( )
import Data.Map as Map
import Data.Char

import System.IO                ( )
import System.Directory
import System.Random
import System.IO.Unsafe

-- import Foreign.C
-- import Foreign

-- | 'eval' provides a typesafe (to a limit) form of runtime evaluation
-- for Haskell -- a limited form of /runtime metaprogramming/. The
-- 'String' argument to 'eval' is a Haskell source fragment to evaluate
-- at rutime. @imps@ are a list of module names to use in the context of
-- the compiled value.
--
-- The value returned by 'eval' is constrained to be 'Typeable' --
-- meaning we can perform a /limited/ runtime typecheck, using the
-- 'dynload' function. One consequence of this is that the code must
-- evaluate to a monomorphic value (which will be wrapped in a
-- 'Dynamic').
--
-- If the evaluated code typechecks under the 'Typeable' constraints,
-- 'Just v' is returned. 'Nothing' indicates typechecking failed.
-- Typechecking may fail at two places: when compiling the argument, or
-- when typechecking the splice point. 'eval' resembles a
-- metaprogramming 'run' operator for /closed/ source fragments.
--
-- To evaluate polymorphic values you need to wrap them in data
-- structures using rank-N types.
--
-- Examples:
--
-- > do i <- eval "1 + 6 :: Int" [] :: IO (Maybe Int)
-- >    when (isJust i) $ putStrLn (show (fromJust i))
--
eval :: Typeable a => String -> [Import] -> IO (Maybe a)
eval :: String -> [String] -> IO (Maybe a)
eval String
src [String]
imps = do
    String
pwd                <- IO String
getCurrentDirectory
    ([String]
cmdline,[String]
loadpath) <- IO ([String], [String])
getPaths
    String
tmpf               <- (String -> String -> [String] -> String)
-> String -> [String] -> IO String
mkUniqueWith String -> String -> [String] -> String
dynwrap String
src [String]
imps
    MakeStatus
status             <- String -> [String] -> IO MakeStatus
make String
tmpf [String]
cmdline
    Maybe a
m_rsrc <- case MakeStatus
status of
        MakeSuccess MakeCode
_ String
obj -> do
           LoadStatus a
m_v <- String -> [String] -> [String] -> String -> IO (LoadStatus a)
forall a.
Typeable a =>
String -> [String] -> [String] -> String -> IO (LoadStatus a)
dynload String
obj [String
pwd] [String]
loadpath String
symbol
           case LoadStatus a
m_v of LoadFailure [String]
_      -> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
                       LoadSuccess Module
_ a
rsrc -> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just a
rsrc
        MakeFailure [String]
err -> (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> IO ()
putStrLn [String]
err IO () -> IO (Maybe a) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
    String -> IO ()
makeCleaner String
tmpf
    Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
m_rsrc

--
-- | 'eval_' is a variety of 'eval' with all the internal hooks
-- available. You are able to set any extra arguments to the compiler
-- (for example, optimisation flags) or dynamic loader, as well as
-- having any errors returned in an 'Either' type.
--
eval_ :: Typeable a =>
         String           -- ^ code to compile
      -> [Import]         -- ^ any imports
      -> [String]         -- ^ extra make flags
      -> [FilePath]       -- ^ (package.confs) for load
      -> [FilePath]       -- ^ include paths load is to search in
      -> IO (Either [String] (Maybe a)) -- ^ either errors, or maybe a well typed value

eval_ :: String
-> [String]
-> [String]
-> [String]
-> [String]
-> IO (Either [String] (Maybe a))
eval_ String
src [String]
mods [String]
args [String]
ldflags [String]
incs = do
    String
pwd                <- IO String
getCurrentDirectory
    ([String]
cmdline,[String]
loadpath) <- IO ([String], [String])
getPaths      -- find path to altdata
    String
tmpf               <- (String -> String -> [String] -> String)
-> String -> [String] -> IO String
mkUniqueWith String -> String -> [String] -> String
dynwrap String
src [String]
mods
    MakeStatus
status             <- String -> [String] -> IO MakeStatus
make String
tmpf ([String] -> IO MakeStatus) -> [String] -> IO MakeStatus
forall a b. (a -> b) -> a -> b
$ [String
"-O0"] [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
cmdline [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [String]
args
    Either [String] (Maybe a)
m_rsrc <- case MakeStatus
status of
        MakeSuccess MakeCode
_ String
obj -> do
           LoadStatus a
m_v <- String -> [String] -> [String] -> String -> IO (LoadStatus a)
forall a.
Typeable a =>
String -> [String] -> [String] -> String -> IO (LoadStatus a)
dynload String
obj (String
pwdString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
incs) ([String]
loadpath[String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++[String]
ldflags) String
symbol
           Either [String] (Maybe a) -> IO (Either [String] (Maybe a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (Maybe a) -> IO (Either [String] (Maybe a)))
-> Either [String] (Maybe a) -> IO (Either [String] (Maybe a))
forall a b. (a -> b) -> a -> b
$ case LoadStatus a
m_v of LoadFailure [String]
e      -> [String] -> Either [String] (Maybe a)
forall a b. a -> Either a b
Left [String]
e
                                LoadSuccess Module
_ a
rsrc -> Maybe a -> Either [String] (Maybe a)
forall a b. b -> Either a b
Right (a -> Maybe a
forall a. a -> Maybe a
Just a
rsrc)
        MakeFailure [String]
err -> Either [String] (Maybe a) -> IO (Either [String] (Maybe a))
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] (Maybe a) -> IO (Either [String] (Maybe a)))
-> Either [String] (Maybe a) -> IO (Either [String] (Maybe a))
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] (Maybe a)
forall a b. a -> Either a b
Left [String]
err
    String -> IO ()
makeCleaner String
tmpf
    Either [String] (Maybe a) -> IO (Either [String] (Maybe a))
forall (m :: * -> *) a. Monad m => a -> m a
return Either [String] (Maybe a)
m_rsrc

-- | Sometimes when constructing string fragments to evaluate, the
-- programmer is able to provide some other constraint on the evaluated
-- string, such that the evaluated expression will be typesafe, without
-- requiring a 'Typeable' constraint. In such cases, the monomorphic
-- restriction is annoying. 'unsafeEval' removes any splice-point
-- typecheck, with an accompanying obligation on the programmer to
-- ensure that the fragment evaluated will be typesafe at the point it
-- is spliced.
--
-- An example of how to do this would be to wrap the fragment in a call
-- to 'show'. The augmented fragment would then be checked when compiled
-- to return a 'String', and the programmer can rely on this, without
-- requiring a splice-point typecheck, and thus no 'Typeable'
-- restriction.
--
-- Note that if you get the proof wrong, your program will likely
-- segfault.
--
-- Example:
--
-- > do s <- unsafeEval "map toUpper \"haskell\"" ["Data.Char"]
-- >    when (isJust s) $ putStrLn (fromJust s)
--
unsafeEval :: String -> [Import] -> IO (Maybe a)
unsafeEval :: String -> [String] -> IO (Maybe a)
unsafeEval String
src [String]
mods = do
    String
pwd    <- IO String
getCurrentDirectory
    String
tmpf   <- (String -> String -> [String] -> String)
-> String -> [String] -> IO String
mkUniqueWith String -> String -> [String] -> String
wrap String
src [String]
mods
    MakeStatus
status <- String -> [String] -> IO MakeStatus
make String
tmpf []
    Maybe a
m_rsrc <- case MakeStatus
status of
        MakeSuccess MakeCode
_ String
obj  -> do
           LoadStatus a
m_v <- String -> [String] -> [String] -> String -> IO (LoadStatus a)
forall a.
String -> [String] -> [String] -> String -> IO (LoadStatus a)
load String
obj [String
pwd] [] String
symbol
           case LoadStatus a
m_v of LoadFailure [String]
_      -> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
                       LoadSuccess Module
_ a
rsrc -> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just a
rsrc
        MakeFailure [String]
err -> (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> IO ()
putStrLn [String]
err IO () -> IO (Maybe a) -> IO (Maybe a)
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
forall a. Maybe a
Nothing
    String -> IO ()
makeCleaner String
tmpf
    Maybe a -> IO (Maybe a)
forall (m :: * -> *) a. Monad m => a -> m a
return Maybe a
m_rsrc

--
-- | 'unsafeEval_' is a form of 'unsafeEval' with all internal hooks
-- exposed. This is useful for application wishing to return error
-- messages to users, to specify particular libraries to link against
-- and so on.
--
unsafeEval_ :: String           -- ^ code to compile
            -> [Import]         -- ^ any imports
            -> [String]         -- ^ make flags
            -> [FilePath]       -- ^ (package.confs) for load
            -> [FilePath]       -- ^ include paths load is to search in
            -> IO (Either [String] a)

unsafeEval_ :: String
-> [String]
-> [String]
-> [String]
-> [String]
-> IO (Either [String] a)
unsafeEval_ String
src [String]
mods [String]
args [String]
ldflags [String]
incs = do
    String
pwd  <- IO String
getCurrentDirectory
    String
tmpf <- (String -> String -> [String] -> String)
-> String -> [String] -> IO String
mkUniqueWith String -> String -> [String] -> String
wrap String
src [String]
mods
    MakeStatus
status <- String -> [String] -> IO MakeStatus
make String
tmpf [String]
args
    Either [String] a
e_rsrc <- case MakeStatus
status of
        MakeSuccess MakeCode
_ String
obj  -> do
           LoadStatus a
m_v <- String -> [String] -> [String] -> String -> IO (LoadStatus a)
forall a.
String -> [String] -> [String] -> String -> IO (LoadStatus a)
load String
obj (String
pwdString -> [String] -> [String]
forall a. a -> [a] -> [a]
:[String]
incs) [String]
ldflags String
symbol
           case LoadStatus a
m_v of LoadFailure [String]
e      -> Either [String] a -> IO (Either [String] a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] a -> IO (Either [String] a))
-> Either [String] a -> IO (Either [String] a)
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] a
forall a b. a -> Either a b
Left [String]
e
                       LoadSuccess Module
_ a
rsrc -> Either [String] a -> IO (Either [String] a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] a -> IO (Either [String] a))
-> Either [String] a -> IO (Either [String] a)
forall a b. (a -> b) -> a -> b
$ a -> Either [String] a
forall a b. b -> Either a b
Right a
rsrc
        MakeFailure [String]
err -> Either [String] a -> IO (Either [String] a)
forall (m :: * -> *) a. Monad m => a -> m a
return (Either [String] a -> IO (Either [String] a))
-> Either [String] a -> IO (Either [String] a)
forall a b. (a -> b) -> a -> b
$ [String] -> Either [String] a
forall a b. a -> Either a b
Left [String]
err
    String -> IO ()
makeCleaner String
tmpf
    Either [String] a -> IO (Either [String] a)
forall (m :: * -> *) a. Monad m => a -> m a
return Either [String] a
e_rsrc
------------------------------------------------------------------------
--
-- | 'mkHsValues' is a helper function for converting 'Data.Map's
-- of names and values into Haskell code. It relies on the assumption of
-- names and values into Haskell code. It relies on the assumption that
-- the passed values' Show instances produce valid Haskell literals
-- (this is true for all Prelude types).
--
mkHsValues :: (Show a) => Map.Map String a -> String
mkHsValues :: Map String a -> String
mkHsValues Map String a
values = [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat ([String] -> String) -> [String] -> String
forall a b. (a -> b) -> a -> b
$ Map String String -> [String]
forall k a. Map k a -> [a]
elems (Map String String -> [String]) -> Map String String -> [String]
forall a b. (a -> b) -> a -> b
$ (String -> a -> String) -> Map String a -> Map String String
forall k a b. (k -> a -> b) -> Map k a -> Map k b
Map.mapWithKey String -> a -> String
forall a. Show a => String -> a -> String
convertToHs Map String a
values
        where convertToHs :: (Show a) => String -> a -> String
              convertToHs :: String -> a -> String
convertToHs String
name a
value = String
name String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" = " String -> String -> String
forall a. [a] -> [a] -> [a]
++ a -> String
forall a. Show a => a -> String
show a
value String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"\n"
------------------------------------------------------------------------
--
-- | Return a compiled value's type, by using Dynamic to get a
-- representation of the inferred type.
--
typeOf :: String -> [Import] -> IO String
typeOf :: String -> [String] -> IO String
typeOf String
src [String]
mods = do
    String
pwd                <- IO String
getCurrentDirectory
    ([String]
cmdline,[String]
loadpath) <- IO ([String], [String])
getPaths
    String
tmpf               <- (String -> String -> [String] -> String)
-> String -> [String] -> IO String
mkUniqueWith String -> String -> [String] -> String
dynwrap String
src [String]
mods
    MakeStatus
status             <- String -> [String] -> IO MakeStatus
make String
tmpf [String]
cmdline
    String
ty <- case MakeStatus
status of
        MakeSuccess MakeCode
_ String
obj -> do
            LoadStatus Dynamic
m_v <- String -> [String] -> [String] -> String -> IO (LoadStatus Dynamic)
forall a.
String -> [String] -> [String] -> String -> IO (LoadStatus a)
load String
obj [String
pwd] [String]
loadpath String
symbol :: IO (LoadStatus Dynamic)
            case LoadStatus Dynamic
m_v of
                LoadFailure [String]
_   -> String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
"<failure>"
                LoadSuccess Module
_ Dynamic
v -> String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> IO String) -> String -> IO String
forall a b. (a -> b) -> a -> b
$ (String -> String
forall a. [a] -> [a]
init (String -> String) -> (String -> String) -> String -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> String
forall a. [a] -> [a]
tail) (String -> String) -> String -> String
forall a b. (a -> b) -> a -> b
$ Dynamic -> String
forall a. Show a => a -> String
show Dynamic
v

        MakeFailure [String]
err -> (String -> IO ()) -> [String] -> IO ()
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ String -> IO ()
putStrLn [String]
err IO () -> IO String -> IO String
forall (m :: * -> *) a b. Monad m => m a -> m b -> m b
>> String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return []
    String -> IO ()
makeCleaner String
tmpf
    String -> IO String
forall (m :: * -> *) a. Monad m => a -> m a
return String
ty

dynwrap :: String -> String -> [Import] -> String
dynwrap :: String -> String -> [String] -> String
dynwrap String
expr String
nm [String]
mods =
        String
"module "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
nmString -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"( resource ) where\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
         (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\String
m-> String
"import "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
mString -> String -> String
forall a. [a] -> [a] -> [a]
++String
"\n") [String]
mods String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
"import Data.Dynamic\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
"resource = let { "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
xString -> String -> String
forall a. [a] -> [a] -> [a]
++String
" = \n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
"{-# LINE 1 \"<eval>\" #-}\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
expr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";} in toDyn "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
x
    where
        x :: String
x = () -> String
ident ()

ident :: () -> String
ident () = IO String -> String
forall a. IO a -> a
unsafePerformIO (IO String -> String) -> IO String -> String
forall a b. (a -> b) -> a -> b
$
            [IO Char] -> IO String
forall (t :: * -> *) (m :: * -> *) a.
(Traversable t, Monad m) =>
t (m a) -> m (t a)
sequence (Int -> [IO Char] -> [IO Char]
forall a. Int -> [a] -> [a]
Prelude.take Int
3 (IO Char -> [IO Char]
forall a. a -> [a]
repeat (IO Char -> [IO Char]) -> IO Char -> [IO Char]
forall a b. (a -> b) -> a -> b
$ (StdGen -> (Int, StdGen)) -> IO Int
forall (m :: * -> *) a. MonadIO m => (StdGen -> (a, StdGen)) -> m a
getStdRandom ((Int, Int) -> StdGen -> (Int, StdGen)
forall a g. (Random a, RandomGen g) => (a, a) -> g -> (a, g)
randomR (Int
97,Int
122)) IO Int -> (Int -> IO Char) -> IO Char
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= Char -> IO Char
forall (m :: * -> *) a. Monad m => a -> m a
return (Char -> IO Char) -> (Int -> Char) -> Int -> IO Char
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> Char
chr))

-- ---------------------------------------------------------------------
-- unsafe wrapper
--
wrap :: String -> String -> [Import] -> String
wrap :: String -> String -> [String] -> String
wrap String
expr String
nm [String]
mods =
        String
"module "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
nmString -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"( resource ) where\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
        (String -> String) -> [String] -> String
forall (t :: * -> *) a b. Foldable t => (a -> [b]) -> t a -> [b]
concatMap (\String
m-> String
"import "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
mString -> String -> String
forall a. [a] -> [a] -> [a]
++String
"\n") [String]
mods String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
"resource = let { "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
xString -> String -> String
forall a. [a] -> [a] -> [a]
++String
" = \n" String -> String -> String
forall a. [a] -> [a] -> [a]
++
        String
"{-# LINE 1 \"<Plugins.Eval>\" #-}\n" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
expr String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
";} in "String -> String -> String
forall a. [a] -> [a] -> [a]
++String
x
    where
        x :: String
x = () -> String
ident ()

-- what is this big variable name?
-- its a random value, so that it won't clash if  the accidentally mistype
-- an unbound 'x' or 'v' in their code.. it won't reveal the internal
-- structure of the wrapper, which is annoying in irc use by lambdabot

{-
------------------------------------------------------------------------
--
-- And for our friends in foreign parts
--
-- TODO needs to accept char** to import list
--

--
-- return NULL pointer if an error occurred.
--

foreign export ccall hs_eval_b  :: CString -> IO (Ptr CInt)
foreign export ccall hs_eval_c  :: CString -> IO (Ptr CChar)
foreign export ccall hs_eval_i  :: CString -> IO (Ptr CInt)
foreign export ccall hs_eval_s  :: CString -> IO CString

------------------------------------------------------------------------
--
-- TODO implement a marshalling for Dynamics, so that we can pass that
-- over to the C side for checking.
--

hs_eval_b :: CString -> IO (Ptr CInt)
hs_eval_b s = do m_v <- eval_cstring s
                 case m_v of Nothing -> return nullPtr
                             Just v  -> new (fromBool v)

hs_eval_c :: CString -> IO (Ptr CChar)
hs_eval_c s = do m_v <- eval_cstring s
                 case m_v of Nothing -> return nullPtr
                             Just v  -> new (castCharToCChar v)

-- should be Integral
hs_eval_i :: CString -> IO (Ptr CInt)
hs_eval_i s = do m_v <- eval_cstring s :: IO (Maybe Int)
                 case m_v of Nothing -> return nullPtr
                             Just v  -> new (fromIntegral v :: CInt)

hs_eval_s :: CString -> IO CString
hs_eval_s s = do m_v <- eval_cstring s
                 case m_v of Nothing -> return nullPtr
                             Just v  -> newCString v

--
-- convenience
--
eval_cstring :: Typeable a => CString -> IO (Maybe a)
eval_cstring cs = do s <- peekCString cs
                     eval s []            -- TODO use eval()

-}