Skip to content

Commit 3a95d82

Browse files
authored
Merge pull request #325 from tweag/gh-272
Support Text literal instances.
2 parents d7a87e0 + a9f54d4 commit 3a95d82

File tree

4 files changed

+42
-3
lines changed

4 files changed

+42
-3
lines changed

inline-r/src/Data/Vector/SEXP.hs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,10 @@ module Data.Vector.SEXP
252252
-- ** SEXP specific helpers.
253253
, toString
254254
, toByteString
255+
, unsafeWithByteString
255256
) where
256257

258+
import Control.Exception (evaluate)
257259
import Control.Monad.R.Class
258260
import Control.Monad.R.Internal
259261
import Control.Memory.Region
@@ -265,7 +267,6 @@ import Foreign.R ( SEXP(..) )
265267
import qualified Foreign.R as R
266268
import Foreign.R.Type ( SEXPTYPE(Char) )
267269

268-
import Control.Monad.Primitive ( PrimMonad )
269270
import Control.Monad.ST (ST, runST)
270271
import Data.Int
271272
import Data.Proxy (Proxy(..))
@@ -274,6 +275,7 @@ import qualified Data.Vector.Generic as G
274275
import Data.Vector.Generic.New (run)
275276
import Data.ByteString ( ByteString )
276277
import qualified Data.ByteString as B
278+
import qualified Data.ByteString.Unsafe as B
277279

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

291-
import Control.Monad.Primitive ( unsafeInlineIO, unsafePrimToPrim )
293+
import Control.Monad.Primitive ( PrimMonad, unsafeInlineIO, unsafePrimToPrim )
294+
import qualified Control.DeepSeq as DeepSeq
292295
import Data.Word ( Word8 )
293296
import Foreign ( Storable, Ptr, castPtr, peekElemOff )
294297
import Foreign.ForeignPtr (ForeignPtr, withForeignPtr)
@@ -455,6 +458,17 @@ toByteString v = unsafeInlineIO $
455458
B.packCStringLen ( castPtr $ unsafeToPtr v
456459
, fromIntegral $ vectorLength v)
457460

461+
-- | This function is unsafe and ByteString should not be used
462+
-- outside of the function. Any change to bytestring will be
463+
-- reflected in the source vector, thus breaking referencial
464+
-- transparancy.
465+
unsafeWithByteString :: DeepSeq.NFData a => Vector s 'Char Word8 -> (ByteString -> IO a) -> a
466+
unsafeWithByteString v f = unsafeInlineIO $ do
467+
x <- B.unsafePackCStringLen (castPtr $ unsafeToPtr v
468+
,fromIntegral $ vectorLength v)
469+
w <- DeepSeq.force <$> f x
470+
evaluate w
471+
458472
------------------------------------------------------------------------
459473
-- Vector API
460474
--

inline-r/src/Foreign/R.hsc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ module Foreign.R
5757
, mkChar
5858
, CEType(..)
5959
, mkCharCE
60+
, mkCharLenCE
6061
, mkWeakRef
6162
-- * Node attributes
6263
, typeOf
@@ -328,6 +329,10 @@ mkCharCE :: CEType -> CString -> IO (SEXP V 'R.Char)
328329
mkCharCE (cIntFromEnum -> ce) value = sexp <$>
329330
[C.exp| SEXP { Rf_mkCharCE($(char * value), $(int ce)) } |]
330331

332+
mkCharLenCE :: CEType -> CString -> Int -> IO (SEXP V 'R.Char)
333+
mkCharLenCE (cIntFromEnum -> ce) value (fromIntegral -> len) = sexp <$>
334+
[C.exp| SEXP { Rf_mkCharLenCE($(char * value), $(int len), $(int ce)) } |]
335+
331336
-- | Intern a string @name@ into the symbol table.
332337
--
333338
-- If @name@ is not found, it is added to the symbol table. The symbol

inline-r/src/Language/R/Literal.hs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,10 @@ import Data.Singletons ( Sing, SingI, fromSing, sing )
5656
import Control.DeepSeq ( NFData )
5757
import Control.Monad ( void, zipWithM_ )
5858
import Data.Int (Int32)
59+
import qualified Data.ByteString.Unsafe as B
5960
import Data.Complex (Complex)
61+
import Data.Text (Text)
62+
import qualified Data.Text.Encoding as T
6063
import Foreign ( FunPtr, castPtr )
6164
import Foreign.C.String ( withCString )
6265
import Foreign.Storable ( Storable, pokeElemOff )
@@ -171,6 +174,19 @@ instance Literal [String] 'R.String where
171174
fromSEXP _ =
172175
failure "fromSEXP" "String expected where some other expression appeared."
173176

177+
instance Literal Text 'R.String where
178+
mkSEXPIO s =
179+
mkSEXPVectorIO sing
180+
[ B.unsafeUseAsCStringLen (T.encodeUtf8 s) $
181+
uncurry (R.mkCharLenCE R.CE_UTF8) ]
182+
fromSEXP (hexp -> String v) =
183+
case SVector.toList v of
184+
[hexp -> Char x] -> SVector.unsafeWithByteString x $ \p -> do
185+
pure $ T.decodeUtf8 p
186+
_ -> failure "fromSEXP" "Not a singleton vector"
187+
fromSEXP _ =
188+
failure "fromSEXP" "String expected where some other expression appeared."
189+
174190
-- | Create a pairlist from an association list. Result is either a pairlist or
175191
-- @nilValue@ if the input is the null list. These are two distinct forms. Hence
176192
-- why the type of this function is not more precise.

inline-r/tests/test-qq.hs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
{-# LANGUAGE ScopedTypeVariables #-}
1010
{-# LANGUAGE TemplateHaskell #-}
1111
{-# LANGUAGE ViewPatterns #-}
12+
{-# LANGUAGE OverloadedStrings #-}
1213

1314
module Main where
1415

@@ -17,6 +18,7 @@ import H.Prelude as H
1718
import qualified Data.Vector.SEXP as SVector
1819
import qualified Data.Vector.SEXP.Mutable as SMVector
1920
import Control.Memory.Region
21+
import Data.Text (Text)
2022

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

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

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

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

0 commit comments

Comments
 (0)