module Scripting.Duktape (
DuktapeCtx
, createDuktapeCtx
, evalDuktape
) where
import Foreign.Ptr
import Foreign.ForeignPtr
import Foreign.Storable
import Foreign.C.String
import Foreign.C.Types
import Foreign.Marshal.Alloc
import Control.Monad.IO.Class
import Data.Text.Encoding (decodeUtf8)
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as BL
import Data.Aeson
import Scripting.Duktape.Raw
#define DUK_TYPE_NONE 0
#define DUK_TYPE_UNDEFINED 1
#define DUK_TYPE_NULL 2
#define DUK_TYPE_BOOLEAN 3
#define DUK_TYPE_NUMBER 4
#define DUK_TYPE_STRING 5
#define DUK_TYPE_OBJECT 6
#define DUK_TYPE_BUFFER 7
#define DUK_TYPE_POINTER 8
#define DUK_TYPE_LIGHTFUNC 9
cMinusOne ∷ CInt
cMinusOne = fromIntegral $ 1
data DukType = DukNone | DukUndefined | DukNull | DukBoolean | DukNumber | DukString | DukObject | DukBuffer | DukPointer | DukLightFunc
getTypeOnStack ∷ DuktapeCtx → Int → IO DukType
getTypeOnStack ctx idx = withForeignPtr ctx $ \ctxPtr → do
t ← c_duk_get_type ctxPtr $ fromIntegral idx
return $ case t of
DUK_TYPE_NONE → DukNone
DUK_TYPE_UNDEFINED → DukUndefined
DUK_TYPE_NULL → DukNull
DUK_TYPE_BOOLEAN → DukBoolean
DUK_TYPE_NUMBER → DukNumber
DUK_TYPE_STRING → DukString
DUK_TYPE_OBJECT → DukObject
DUK_TYPE_BUFFER → DukBuffer
DUK_TYPE_POINTER → DukPointer
DUK_TYPE_LIGHTFUNC → DukLightFunc
_ → DukNone
getStringFromStack ∷ DuktapeCtx → Int → IO BS.ByteString
getStringFromStack ctx idx = withForeignPtr ctx $ \ctxPtr → do
let cIdx = fromIntegral idx
lenPtr ← malloc ∷ IO (Ptr CSize)
str ← c_duk_get_lstring ctxPtr cIdx lenPtr
len ← peek lenPtr
retVal ← BS.packCStringLen (str, fromIntegral len)
free lenPtr
return retVal
getValueFromStack ∷ DuktapeCtx → Int → IO (Maybe Value)
getValueFromStack ctx idx = withForeignPtr ctx $ \ctxPtr → do
retType ← getTypeOnStack ctx idx
let cIdx = fromIntegral idx
ret = return . Just
case retType of
DukNull → ret Null
DukBoolean → c_duk_get_boolean ctxPtr cIdx >>= ret . Bool . (== 1) . fromIntegral
DukNumber → c_duk_get_number ctxPtr cIdx >>= ret . Number . realToFrac
DukString → getStringFromStack ctx idx >>= ret . String . decodeUtf8
DukObject → c_duk_json_encode ctxPtr cIdx >> getStringFromStack ctx idx >>= return . decode . BL.fromStrict
_ → return Nothing
createDuktapeCtx ∷ MonadIO μ ⇒ μ (Maybe DuktapeCtx)
createDuktapeCtx = liftIO $ createHeapF nullFunPtr
evalDuktape ∷ MonadIO μ ⇒ DuktapeCtx → BS.ByteString → μ (Either String (Maybe Value))
evalDuktape ctx src =
liftIO $ withForeignPtr ctx $ \ctxPtr →
BS.useAsCStringLen src $ \(srcCstr, srcLen) → do
evalCode ← c_duk_peval_lstring ctxPtr srcCstr $ fromIntegral srcLen
retVal ← if evalCode /= fromIntegral 0
then c_duk_safe_to_string ctxPtr cMinusOne >>= peekCString >>= return . Left
else getValueFromStack ctx (1) >>= return . Right
c_duk_pop ctxPtr
return retVal