j 0.1.0.0 → 0.1.1.0
raw patch · 4 files changed
+76/−18 lines, 4 filesdep +Win32PVP ok
version bump matches the API change (PVP)
Dependencies added: Win32
API changes (from Hackage documentation)
Files
- CHANGELOG.md +5/−0
- j.cabal +11/−5
- src/Language/J.hs +38/−8
- test/Spec.hs +22/−5
CHANGELOG.md view
@@ -1,5 +1,10 @@ # j +## 0.1.1.0++ * Add `libWindows`+ * Support for Windows+ ## 0.1.0.0 Initial release
j.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: j-version: 0.1.0.0+version: 0.1.1.0 license: BSD3 license-file: LICENSE copyright: Copyright: (c) 2020 Vanessa McHale@@ -22,14 +22,19 @@ exposed-modules: Language.J hs-source-dirs: src default-language: Haskell2010- other-extensions: FlexibleContexts OverloadedStrings CPP+ other-extensions: FlexibleContexts OverloadedStrings ghc-options: -Wall build-depends: base >=4.7 && <5,- unix -any,- bytestring -any,- repa -any+ bytestring >=0.10,+ repa >=3.2.5.0 + if !os(windows)+ build-depends: unix >=2.7.0.0++ else+ build-depends: Win32 -any+ if !impl(ghc >=8.0) build-depends: semigroups -any @@ -52,6 +57,7 @@ main-is: Spec.hs hs-source-dirs: test default-language: Haskell2010+ other-extensions: CPP OverloadedStrings ghc-options: "-with-rtsopts -K1K" -Wall build-depends: base -any,
src/Language/J.hs view
@@ -46,7 +46,7 @@ -- 3. Perform calculations within the J environment. Here, we use -- 'bsDispatch' to compute some results and assign them within J ----- 4. Marshal J values back to Haskell. We use 'getJData' again.+-- 4. Marshal J values back to Haskell. We use 'getJData'. -- -- -- Since marshaling data between J and Haskell is expensive, it's best to do as@@ -58,8 +58,12 @@ module Language.J ( -- * Environment JEnv (..) , jinit+#ifndef mingw32_HOST_OS , libLinux , libMac+#else+ , libWindows+#endif , bsDispatch , bsOut , JVersion@@ -87,9 +91,14 @@ import Foreign.C.Types (CChar, CDouble, CInt (..), CLLong (..)) import Foreign.ForeignPtr (ForeignPtr, castForeignPtr, mallocForeignPtrBytes, withForeignPtr) import Foreign.Marshal (alloca, copyArray, mallocBytes, peekArray, pokeArray)-import Foreign.Ptr (FunPtr, Ptr, plusPtr)+import Foreign.Ptr (FunPtr, Ptr, castPtrToFunPtr, plusPtr) import Foreign.Storable (Storable, peek, pokeByteOff, sizeOf)+import System.Info (arch)+#ifndef mingw32_HOST_OS import System.Posix.ByteString (RTLDFlags (RTLD_LAZY), RawFilePath, dlopen, dlsym)+#else+import System.Win32.DLL (getProcAddress, loadLibrary)+#endif -- Upstream reference -- https://github.com/jsoftware/stats_jserver4r/blob/4c94fc6df351fab34791aa9d78d158eaefd33b17/source/lib/j2r.c@@ -116,21 +125,29 @@ foreign import ccall "dynamic" mkJGetR :: FunPtr JGetRType -> JGetRType foreign import ccall "dynamic" mkJSetA :: FunPtr JSetAType -> JSetAType +type JVersion = [Int]++squashVersion :: JVersion -> String+squashVersion = concatMap show++#ifndef mingw32_HOST_OS -- | Expected 'RawFilePath' to the library on a Linux machine. libLinux :: RawFilePath-libLinux = "/usr/lib/x86_64-linux-gnu/libj.so"--type JVersion = [Int]+libLinux = "/usr/lib/" <> ASCII.pack arch <> "-linux-gnu/libj.so" -- | Expected 'RawFilePath' to the library on Mac. libMac :: JVersion -> RawFilePath-libMac v = "/Applications/j64-" <> ASCII.pack (concatMap show v) <> "/bin/libj.dylib"---- process address+libMac v = "/Applications/j64-" <> ASCII.pack (squashVersion v) <> "/bin/libj.dylib"+#else+-- | @since 0.1.1.0+libWindows :: JVersion -> FilePath+libWindows v = "C:\\Program Files\\J" <> squashVersion v <> "\\bin\\j.dll"+#endif -- | Get a J environment -- -- Passing the resultant 'JEnv' between threads can cause unexpected bugs.+#ifndef mingw32_HOST_OS jinit :: RawFilePath -- ^ Path to J library -> IO JEnv jinit libFp = do@@ -141,6 +158,19 @@ let jOut = mkJGetR <$> dlsym libj "JGetR" let jSet = mkJSetA <$> dlsym libj "JSetA" JEnv jt <$> jeval <*> jread <*> jOut <*> jSet+#else+jinit :: FilePath+ -> IO JEnv+jinit libFp = do+ libj <- loadLibrary libFp+ jt <- mkJInit . castPtrToFunPtr =<< getProcAddress libj "JInit"+ let jeval = mkJDo . castPtrToFunPtr <$> getProcAddress libj "JDo"+ let jread = mkJGetM . castPtrToFunPtr <$> getProcAddress libj "JGetM"+ let jOut = mkJGetR . castPtrToFunPtr <$> getProcAddress libj "JGetR"+ let jSet = mkJSetA . castPtrToFunPtr <$> getProcAddress libj "JSetA"+ JEnv jt <$> jeval <*> jread <*> jOut <*> jSet+#endif+ -- | Send some J code to the environment. bsDispatch :: JEnv -> BS.ByteString -> IO ()
test/Spec.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} {-# LANGUAGE OverloadedStrings #-} module Main ( main ) where@@ -12,7 +13,17 @@ main :: IO () main = do+#ifdef linux_HOST_OS jenv <- jinit libLinux+#else+#ifdef darwin_HOST_OS+ jenv <- jinit (libMac [8,0,7])+#else+#ifdef mingw32_HOST_OS+ jenv <- jinit (libWindows [9,0,1])+#endif+#endif+#endif defaultMain $ testGroup "J dl" [ testCase "Performs calculation and has sensible output" (jComp jenv)@@ -20,9 +31,19 @@ , testCase "Reads a string" (jStr jenv) , testCase "Sends an array to J" (jSetA jenv) , testCase "Uses J to perform a complex calculation" (regress jenv)- , testCase "Writes strigns to J values" (stringRoundtrip jenv)+ , testCase "Writes strings to J values" (stringRoundtrip jenv)+ -- , testCase "Uses J for something Haskell would have a hard time with" (fill jenv) ] ++fill :: JEnv -> Assertion+fill jenv = do+ bsDispatch jenv "random_res =: ? 70 70 $ 1e10"+ res <- getJData jenv "random_res"+ extrExtent res @?= [70, 70]+ where extrExtent :: JData R.DIM2 -> [Int]+ extrExtent (JIntArr res) = R.listOfShape $ R.extent res+ regress :: JEnv -> Assertion regress jenv = do let hsArr0 = R.fromListUnboxed (R.ix1 3) [1.0,2.0,3.0]@@ -61,19 +82,15 @@ unwrapStr :: JData R.Z -> BS.ByteString unwrapStr (JString bs) = bs-unwrapStr _ = error "Test suite error." doubleVect :: JData R.DIM1 -> [CDouble] doubleVect (JDoubleArr arr) = R.toList arr-doubleVect _ = error "Test suite failure!" doubleScalar :: JData R.Z -> [CDouble] doubleScalar (JDoubleArr arr) = R.toList arr-doubleScalar _ = error "Test suite failure!" intList :: JData R.DIM1 -> [CInt] intList (JIntArr arr) = R.toList arr-intList _ = error "Test suite failure!" jType :: JEnv -> Assertion jType jenv = do