Skip to content

Commit

Permalink
Merge pull request #325 from tweag/gh-272
Browse files Browse the repository at this point in the history
Support Text literal instances.
  • Loading branch information
mboes authored Jun 9, 2018
2 parents d7a87e0 + a9f54d4 commit 3a95d82
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 3 deletions.
18 changes: 16 additions & 2 deletions inline-r/src/Data/Vector/SEXP.hs
Original file line number Diff line number Diff line change
Expand Up @@ -252,8 +252,10 @@ module Data.Vector.SEXP
-- ** SEXP specific helpers.
, toString
, toByteString
, unsafeWithByteString
) where

import Control.Exception (evaluate)
import Control.Monad.R.Class
import Control.Monad.R.Internal
import Control.Memory.Region
Expand All @@ -265,7 +267,6 @@ import Foreign.R ( SEXP(..) )
import qualified Foreign.R as R
import Foreign.R.Type ( SEXPTYPE(Char) )

import Control.Monad.Primitive ( PrimMonad )
import Control.Monad.ST (ST, runST)
import Data.Int
import Data.Proxy (Proxy(..))
Expand All @@ -274,6 +275,7 @@ import qualified Data.Vector.Generic as G
import Data.Vector.Generic.New (run)
import Data.ByteString ( ByteString )
import qualified Data.ByteString as B
import qualified Data.ByteString.Unsafe as B

import Control.Applicative hiding (empty)
#if MIN_VERSION_vector(0,11,0)
Expand All @@ -288,7 +290,8 @@ import qualified Data.Vector.Fusion.Stream as Stream
import qualified Data.Vector.Fusion.Stream.Monadic as MStream
#endif

import Control.Monad.Primitive ( unsafeInlineIO, unsafePrimToPrim )
import Control.Monad.Primitive ( PrimMonad, unsafeInlineIO, unsafePrimToPrim )
import qualified Control.DeepSeq as DeepSeq
import Data.Word ( Word8 )
import Foreign ( Storable, Ptr, castPtr, peekElemOff )
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
Expand Down Expand Up @@ -455,6 +458,17 @@ toByteString v = unsafeInlineIO $
B.packCStringLen ( castPtr $ unsafeToPtr v
, fromIntegral $ vectorLength v)

-- | This function is unsafe and ByteString should not be used
-- outside of the function. Any change to bytestring will be
-- reflected in the source vector, thus breaking referencial
-- transparancy.
unsafeWithByteString :: DeepSeq.NFData a => Vector s 'Char Word8 -> (ByteString -> IO a) -> a
unsafeWithByteString v f = unsafeInlineIO $ do
x <- B.unsafePackCStringLen (castPtr $ unsafeToPtr v
,fromIntegral $ vectorLength v)
w <- DeepSeq.force <$> f x
evaluate w

------------------------------------------------------------------------
-- Vector API
--
Expand Down
5 changes: 5 additions & 0 deletions inline-r/src/Foreign/R.hsc
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ module Foreign.R
, mkChar
, CEType(..)
, mkCharCE
, mkCharLenCE
, mkWeakRef
-- * Node attributes
, typeOf
Expand Down Expand Up @@ -328,6 +329,10 @@ mkCharCE :: CEType -> CString -> IO (SEXP V 'R.Char)
mkCharCE (cIntFromEnum -> ce) value = sexp <$>
[C.exp| SEXP { Rf_mkCharCE($(char * value), $(int ce)) } |]

mkCharLenCE :: CEType -> CString -> Int -> IO (SEXP V 'R.Char)
mkCharLenCE (cIntFromEnum -> ce) value (fromIntegral -> len) = sexp <$>
[C.exp| SEXP { Rf_mkCharLenCE($(char * value), $(int len), $(int ce)) } |]

-- | Intern a string @name@ into the symbol table.
--
-- If @name@ is not found, it is added to the symbol table. The symbol
Expand Down
16 changes: 16 additions & 0 deletions inline-r/src/Language/R/Literal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,10 @@ import Data.Singletons ( Sing, SingI, fromSing, sing )
import Control.DeepSeq ( NFData )
import Control.Monad ( void, zipWithM_ )
import Data.Int (Int32)
import qualified Data.ByteString.Unsafe as B
import Data.Complex (Complex)
import Data.Text (Text)
import qualified Data.Text.Encoding as T
import Foreign ( FunPtr, castPtr )
import Foreign.C.String ( withCString )
import Foreign.Storable ( Storable, pokeElemOff )
Expand Down Expand Up @@ -171,6 +174,19 @@ instance Literal [String] 'R.String where
fromSEXP _ =
failure "fromSEXP" "String expected where some other expression appeared."

instance Literal Text 'R.String where
mkSEXPIO s =
mkSEXPVectorIO sing
[ B.unsafeUseAsCStringLen (T.encodeUtf8 s) $
uncurry (R.mkCharLenCE R.CE_UTF8) ]
fromSEXP (hexp -> String v) =
case SVector.toList v of
[hexp -> Char x] -> SVector.unsafeWithByteString x $ \p -> do
pure $ T.decodeUtf8 p
_ -> failure "fromSEXP" "Not a singleton vector"
fromSEXP _ =
failure "fromSEXP" "String expected where some other expression appeared."

-- | Create a pairlist from an association list. Result is either a pairlist or
-- @nilValue@ if the input is the null list. These are two distinct forms. Hence
-- why the type of this function is not more precise.
Expand Down
6 changes: 5 additions & 1 deletion inline-r/tests/test-qq.hs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE ViewPatterns #-}
{-# LANGUAGE OverloadedStrings #-}

module Main where

Expand All @@ -17,6 +18,7 @@ import H.Prelude as H
import qualified Data.Vector.SEXP as SVector
import qualified Data.Vector.SEXP.Mutable as SMVector
import Control.Memory.Region
import Data.Text (Text)

import Control.Applicative
import Control.Monad.Trans (liftIO)
Expand Down Expand Up @@ -108,9 +110,11 @@ main = H.withEmbeddedR H.defaultConfig $ H.runRegion $ do
("c(7, 2, 3)" @=?) =<< [r| v = v2_hs; v[1] <- 7; v |]
io . assertEqual "" "fromList [1,2,3]" . Prelude.show =<< SVector.unsafeFreeze v1

let utf8string = "abcd çéõßø"
let utf8string = "abcd çéõßø" :: String
io . assertEqual "" utf8string =<< fromSEXP <$> R.cast (sing :: R.SSEXPTYPE 'R.String) <$> [r| utf8string_hs |]

let utf8string1 = "abcd çéõßø" :: Text
io . assertEqual "" utf8string1 =<< fromSEXP <$> R.cast (sing :: R.SSEXPTYPE 'R.String) <$> [r| utf8string1_hs |]

-- Disable gctorture, otherwise test takes too long to execute.
_ <- [r| gctorture2(0) |]
Expand Down

0 comments on commit 3a95d82

Please sign in to comment.