Skip to content
Browse files

split of Haskell code into its own repo, for easier integration with …

…Cabal
  • Loading branch information...
1 parent 516d8ef commit ccf024e17384e049c171a1b8aa8ad1a1177de0fe @mwotton committed
View
6 .gitignore
@@ -1,7 +1,5 @@
.jhci-hist
-stupid emacs lock files
.#*
-jhc stuff
hs.out_code.c
hs.out
examples/simple_inline/RubyMap.hs
@@ -24,3 +22,7 @@ lib/RubyMap.o
lib/rshim.o
sample/tmp
sample/tmp.old
+Setup
+dist
+*.hi
+*.o
View
7 Hubrify.hs
@@ -0,0 +1,7 @@
+module Main where
+import Language.Ruby.Hubris.LibraryBuilder
+import System
+
+main = do
+ (mod:extra_src) <- getArgs
+ generateLib extra_src mod >>= print
View
21 INSTALL
@@ -0,0 +1,21 @@
+This is a bit involved at the moment, because you need to have a development version of GHC,
+as well as having a bootstrapping version of GHC to build it with.
+
+So, on Ubuntu,
+
+sudo apt-get install ruby ruby-dev ghc libopenssl-ruby
+sudo gem install rake open4 rspec hoe
+cabal install c2hs
+wget http://www.haskell.org/ghc/dist/current/dist/ghc-6.11.20090907-src.tar.bz2
+tar -jxvf ghc-6.11.20090907-src.tar.bz2
+cd ghc-6.11.20090907
+# adjust the argument to -j to your number of cores, and the prefix if you need to install somewhere else
+sh boot && ./configure --enable-shared --prefix=/usr/local && make -j 4 && sudo make install
+cd ..
+git clone git://github.com/mwotton/Hubris.git
+cd Hubris
+rake
+# here's where you'll see a whole lot of successes, if you're very lucky
+# There's a good chance you won't. Tell me what went wrong and i'll fix the docs.
+spec spec/*_spec.rb
+
View
22 LICENSE
@@ -0,0 +1,22 @@
+(The MIT License)
+
+Copyright (c) 2009 Mark Wotton
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+'Software'), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
19 Language/Ruby/Foo.hs
@@ -0,0 +1,19 @@
+module Foo (wrap, testString, wrap2, external, can_be_called, cannot_be_called) where
+can_be_called :: Int -> Int
+can_be_called x = 2 * x
+
+could_be_called :: Int -> Int
+could_be_called x = 3 * x
+wrap :: Int -> Int
+wrap x = 3 - x
+
+wrap2 :: Int -> Int
+wrap2 x = 3 - x
+
+cannot_be_called = map
+
+internal = 1
+external = 10
+
+testString :: Int -> String
+testString x = show x
View
104 Language/Ruby/Hubris.hs
@@ -0,0 +1,104 @@
+{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
+module Language.Ruby.Hubris where
+
+import Data.Word
+import Data.Map as Map
+-- import Language.Ruby.Hubris.Binding
+import System.IO.Unsafe (unsafePerformIO)
+import Foreign.C.Types
+import Language.Ruby.Hubris.Binding
+import Control.Monad (forM)
+-- type Value = CULong
+
+
+
+wrap :: (Haskellable a, Rubyable b) => (a->b) -> (Value -> Value)
+wrap func ar = fromRVal $ case (toHaskell $ fromVal ar) of
+ Just a -> toRuby $ func a
+ Nothing -> T_NIL
+
+-- fromVal :: Value -> RValue
+-- fromRVal ::RValue -> Value
+-- fromVal = undefined
+-- fromRVal = undefined
+
+class Haskellable a where
+ toHaskell :: RValue -> Maybe a
+
+class Rubyable a where
+ toRuby :: a -> RValue
+
+instance Haskellable Int where
+ toHaskell (T_FIXNUM i) = Just i
+ toHaskell _ = Nothing
+
+instance Rubyable Int where
+ toRuby i = T_FIXNUM i
+
+instance Haskellable Integer where
+ toHaskell (T_BIGNUM i) = Just i
+ toHaskell _ = Nothing
+
+instance Rubyable Integer where
+ toRuby i = T_BIGNUM i
+
+instance Haskellable Bool where
+ toHaskell T_TRUE = Just True
+ toHaskell T_FALSE = Just False
+ toHaskell _ = Nothing
+
+instance Rubyable Bool where
+ toRuby True = T_TRUE
+ toRuby False = T_FALSE
+
+instance Rubyable Double where
+ toRuby d = T_FLOAT d
+
+instance Haskellable Double where
+ toHaskell (T_FLOAT f) = Just f
+ toHaskell (T_FIXNUM i) = Just $ fromIntegral i -- does this make sense?
+ toHaskell _ = Nothing
+
+instance Haskellable String where
+ toHaskell (T_STRING s) = Just s
+ toHaskell _ = Nothing
+instance Rubyable String where
+ toRuby s = (T_STRING s)
+
+
+-- This is a tricky case.
+-- The ruby FFI wants us to pass a C callback which it can apply to each key-value pair
+-- of the hash, so Haskell cannot be fully in control of the process - this makes building
+-- up a Data.Map object in the natural way a bit tricky.
+
+-- current thoughts:
+-- 1. write a direct binding to the ruby API, include a C level function for getting the keys.
+-- just eat the cost of transferring through a keys call + looping over the elements.
+-- One big benefit - while iteration is expensive, using it as a hash table should be cheap
+-- (although probably needs to stay in the IO monad, which is less convenient.)
+--
+-- 2. write a binding to the Judy library that creates a Judy object directly. If we can convince
+-- HsJudy to accept that, then we're golden - we still have to copy over, but keys operations
+-- should be cheap (and hopefully lazy, but test to make sure).
+--
+-- These are of course not mutually exclusive.
+--
+-- The first should probably be a part of the base package. The second needs access to internals,
+-- but should probably be an optional package. This means that in Hubris.Internals, we should expose
+
+-- > rb_foreach :: Value {- HASH -} -> (CFunction ((Key,Value,a) -> a)) -> a -> IO a
+--
+--
+
+-- instance Haskellable (Map.Map a b ) where
+-- toHaskell (T_HASH s) = unsafePerformIO $
+-- get_each
+
+-- toHaskell _ = Nothing
+
+instance (Rubyable a, Rubyable b) => Rubyable (Map.Map a b) where
+ toRuby s = unsafePerformIO $
+ do hash <- rb_hash_new
+ forM (toList s)
+ (\(k,v) -> rb_hash_aset hash (fromRVal $ toRuby k) (fromRVal $ toRuby v))
+ return $ T_HASH hash
View
38 lib/RubyMap.chs → Language/Ruby/Hubris/Binding.chs
@@ -1,5 +1,4 @@
-{-# LANGUAGE ForeignFunctionInterface #-}
--- {-# LANGUAGE TypeSynonymInstances #-}
+{-# LANGUAGE ForeignFunctionInterface, TypeSynonymInstances #-}
{- TODO
@@ -10,10 +9,13 @@ Cabal, that leaves JHC out in the cold.
perhaps need cabal file for ghc and equivalent for jhc.
+also: do we want to support different versions of ruby? for the moment, you just get
+whichever ruby.h is first in the search place.
+
-}
-module RubyMap where
+module Language.Ruby.Hubris.Binding where
#include "rshim.h"
#include <ruby.h>
@@ -58,6 +60,7 @@ foreign import ccall unsafe "ruby.h rb_ary_push" rb_ary_push :: Value -> V
foreign import ccall unsafe "ruby.h rb_float_new" rb_float_new :: Double -> Value
foreign import ccall unsafe "ruby.h rb_big2str" rb_big2str :: Value -> Int -> Value
foreign import ccall unsafe "ruby.h rb_str_to_inum" rb_str_to_inum :: Value -> Int -> Int -> Value
+foreign import ccall unsafe "ruby.h ruby_init" ruby_init :: IO ()
-- we're being a bit filthy here - the interface is all macros, so we're digging in to find what it actually is
foreign import ccall unsafe "rshim.h rb_ary_len" rb_ary_len :: Value -> CUInt
@@ -65,6 +68,7 @@ foreign import ccall unsafe "rshim.h rtype" rtype :: Value -> Int
foreign import ccall unsafe "rshim.h int2fix" int2fix :: Int -> Value
foreign import ccall unsafe "rshim.h fix2int" fix2int :: Value -> Int
foreign import ccall unsafe "rshim.h num2dbl" num2dbl :: Value -> Double -- technically CDoubles, but jhc promises they're the same
+foreign import ccall unsafe "rshim.h keys" rb_keys :: Value -> IO Value
-- this line crashes jhc
foreign import ccall unsafe "intern.h rb_ary_entry" rb_ary_entry :: Value -> CLong -> IO Value
@@ -72,6 +76,9 @@ foreign import ccall unsafe "intern.h rb_ary_entry" rb_ary_entry :: Value -> CLo
foreign import ccall safe "ruby.h rb_raise" rb_raise :: Value -> CString -> IO ()
foreign import ccall unsafe "ruby.h rb_eval_string" rb_eval_string :: CString -> Value
+foreign import ccall unsafe "intern.h rb_hash_aset" rb_hash_aset :: Value -> Value -> Value -> IO ()
+foreign import ccall unsafe "intern.h rb_hash_new" rb_hash_new :: IO Value
+foreign import ccall unsafe "intern.h rb_hash_aref" rb_hash_aref :: Value -> Value -> IO Value
@@ -83,7 +90,7 @@ data RValue = T_NIL
-- to be an extraction step to pull the RValues out.
| T_ARRAY [RValue]
| T_FIXNUM Int
- | T_HASH Int -- definitely FIXME - native ruby hashes, or going to transliterate into Data.Map?
+ | T_HASH Value -- Int -- definitely FIXME - native ruby hashes, or going to transliterate into Data.Map?
| T_BIGNUM Integer
-- technically, these are mapping over the types True and False,
@@ -92,7 +99,7 @@ data RValue = T_NIL
| T_FALSE
| T_SYMBOL Word -- interned string
-
+ deriving (Eq, Show)
-- These are the other basic Ruby structures that we're not handling yet.
-- | T_REGEXP
-- | T_FILE
@@ -101,9 +108,9 @@ data RValue = T_NIL
-- | T_OBJECT
-- | T_CLASS
-- | T_MODULE
-
-toRuby :: RValue -> Value
-toRuby r = case r of
+-- leaky as hell
+fromRVal :: RValue -> Value
+fromRVal r = case r of
T_FLOAT d -> rb_float_new d
-- need to take the address of the cstr, just cast it to a value
-- sadly no bytestrings yet - unpack it to a list. yeah it's ugly.
@@ -115,15 +122,16 @@ toRuby r = case r of
T_TRUE -> fromIntegral $ fromEnum RUBY_Qtrue
T_FALSE -> fromIntegral $ fromEnum RUBY_Qfalse
T_NIL -> fromIntegral $ fromEnum RUBY_Qnil
+ T_HASH h -> h
T_ARRAY l -> unsafePerformIO $ do
ary <- rb_ary_new2 $ fromIntegral $ length l
- mapM_ (rb_ary_push ary . toRuby) l
+ mapM_ (rb_ary_push ary . fromRVal) l
return ary
T_BIGNUM b -> rb_str_to_inum (rb_str_new2 $ unsafePerformIO $ newCAString $ show b) 10 1
_ -> error "sorry, haven't implemented that yet."
-fromRuby :: Value -> RValue
-fromRuby v = case target of
+fromVal :: Value -> RValue
+fromVal v = case target of
RT_NIL -> T_NIL
RT_FIXNUM -> T_FIXNUM $ fix2int v
RT_STRING -> T_STRING $ unsafePerformIO $ peekCString $ rb_str2cstr v 0
@@ -131,14 +139,16 @@ fromRuby v = case target of
RT_BIGNUM -> T_BIGNUM $ read $ unsafePerformIO $ peekCString $ rb_str2cstr (rb_big2str v 10) 0
RT_TRUE -> T_TRUE
RT_FALSE -> T_FALSE
- RT_ARRAY -> T_ARRAY $ map fromRuby $ unsafePerformIO $ mapM (rb_ary_entry v . fromIntegral) [0..(rb_ary_len v) - 1]
+ RT_HASH -> T_HASH v
+ RT_ARRAY -> T_ARRAY $ map fromVal $ unsafePerformIO $ mapM (rb_ary_entry v . fromIntegral) [0..(rb_ary_len v) - 1]
- _ -> error (show target)
+ _ -> error "didn't work" -- (show target)
where target :: RubyType
target = toEnum $ rtype v
--- utility stuff
+unsafeThrow :: String -> a
+unsafeThrow s = unsafePerformIO $ throwException s >> undefined
throwException :: String -> IO Value
throwException s = do he <- newCAString "HaskellError"
err <- newCAString s
View
51 Language/Ruby/Hubris/GHCBuild.hs
@@ -0,0 +1,51 @@
+module Language.Ruby.Hubris.GHCBuild (ghcBuild, defaultGHCOptions, GHCOptions(..)) where
+import Config
+import Debug.Trace
+import DynFlags
+import GHC
+import GHC.Paths
+import Outputable
+import StringBuffer
+import System.Time
+import Control.Monad(forM_)
+
+newtype GHCOptions = GHCOptions { strict :: Bool }
+defaultGHCOptions = GHCOptions { strict = True }
+type Filename = String
+
+
+sh = showSDoc . ppr
+-- this one's a bit tricky: we _could_ use the GHC api, but i don't care too much.
+-- let's keep it simple.
+--
+-- ok, new plan: handling it the filthy way is awful.
+ghcBuild :: Filename -> String -> String -> [Filename] -> GHCOptions -> IO Bool
+ghcBuild libFile immediateSource modName extra_sources options =
+ do srcBuffer <- stringToStringBuffer immediateSource
+ putStrLn ("modname is " ++ modName)
+ writeFile "/tmp/foo.hs" immediateSource
+ time <- getClockTime -- i suppose we've just made it now...
+ defaultErrorHandler defaultDynFlags $ do
+ res <- runGhc (Just libdir) $ do
+ dflags <- getSessionDynFlags
+
+ (newflags, leftovers, warnings) <- GHC.parseDynamicFlags dflags
+ $ map noLoc $ (words $ "-shared -o " ++ libFile ++ " -optl-Wl,-rpath," ++ libdir
+ ++ " /Users/mwotton/projects/Hubris/lib/rshim.o -lHSrts-ghc" ++ Config.cProjectVersion)
+ trace ("left: " ++ sh leftovers) $ trace (sh warnings) $ setSessionDynFlags newflags
+
+ forM_ ("/tmp/foo.hs":extra_sources) (\file -> guessTarget file Nothing >>= addTarget)
+
+ load LoadAllTargets
+ -- do something with c_sources and extra_sources
+-- setTargets [Target { targetContents = Just ( srcBuffer, time ),
+-- targetAllowObjCode = True,
+-- targetId = TargetModule $ mkModuleName modName
+-- } ]
+
+ -- } ]
+
+ return (case res of
+ Succeeded -> True
+ _ -> False)
+
View
27 Language/Ruby/Hubris/Hash.hs
@@ -0,0 +1,27 @@
+module Language.Ruby.Hubris.Hash where
+import qualified Language.Ruby.Hubris.Binding as Ruby
+import Prelude hiding(lookup)
+import Control.Applicative
+
+newtype RubyHash = RubyHash Ruby.Value
+
+-- can only call these functions when we have a ruby interpreter
+-- initialised. shouldn't usually be a problem, but needs to be
+-- done when testing from haskell.
+
+-- to test: does this break horribly when we have multiple threads?
+
+new = RubyHash <$> Ruby.rb_hash_new
+insert (RubyHash v) = Ruby.rb_hash_aset v
+
+-- no Maybe here - we'd just need to test again later, as we're passing
+-- a Ruby value
+lookup (RubyHash v) key = Ruby.rb_hash_aref v key
+
+-- maybe should extract strings?
+keys :: RubyHash -> IO [Ruby.RValue]
+keys (RubyHash v) = do Ruby.T_ARRAY res <- Ruby.fromVal <$> Ruby.rb_keys v
+ return res
+
+toList rhash = keys rhash >>= mapM (\k -> lookup rhash (Ruby.fromRVal k) >>= \v -> return (k,v))
+
View
7 Language/Ruby/Hubris/Hubrify.hs
@@ -0,0 +1,7 @@
+module Main where
+import Language.Ruby.Hubris.LibraryBuilder
+import System
+
+main = do
+ (mod:extra_src) <- getArgs
+ print $ generateLib extra_src mod
View
22 Language/Ruby/Hubris/Interpolator.hs
@@ -0,0 +1,22 @@
+{-# LANGUAGE TemplateHaskell #-}
+
+module Interpolator where
+import Language.Haskell.TH
+import Language.Haskell.TH.Syntax
+import Language.Haskell.Meta.Parse
+
+-- The first string in each pair is literal, the second is a variable to
+-- be interpolated.
+parse :: String -> [(String, String)]
+-- parse "Foo#{foo}rah#{foo}" = [("Foo", "foo ++ bar"), ("rah", "foo")]
+parse str =
+gen :: [(String, String)] -> Q Exp -> Q Exp
+gen [] x = x
+-- gen ((string,variable) : xs) x = gen xs [| $x ++ $(lift string) ++ $(return $ VarE $ mkName variable) |]
+gen ((string,expr) : xs) x = gen xs [| $x ++ $(lift string) ++ $(return $ lift $ parseExp expr) |]
+-- gen ((string,variable) : xs) x = gen xs [| $x ++ $(lift string) ++ $(stringE variable) |]
+
+-- Here we generate the Haskell code for the splice
+-- from an input format string.
+interpolate :: String -> Q Exp
+interpolate s = gen (parse s) [| "" |]
View
80 Language/Ruby/Hubris/LibraryBuilder.hs
@@ -0,0 +1,80 @@
+{-# LANGUAGE TemplateHaskell, QuasiQuotes #-}
+module Language.Ruby.Hubris.LibraryBuilder (generateLib) where
+import Language.Ruby.Hubris.ZCode (zenc, zdec)
+import Language.Ruby.Hubris
+import Language.Haskell.Interpreter
+import Language.Haskell.Meta.QQ.HsHere
+import Language.Ruby.Hubris.GHCBuild
+
+import Debug.Trace
+import Control.Monad
+import Control.Monad.Error.Class
+
+import GHC(parseStaticFlags, noLoc)
+type Filename = String
+
+generateLib :: [Filename] -> ModuleName -> IO (Maybe Filename)
+generateLib sources moduleName = do
+ -- set up the static args once
+ GHC.parseStaticFlags $ map noLoc $ words "-dynamic -fPIC -package hubris"
+
+ let libFile = zenc ("libHubris_" ++ moduleName)
+ s <- generateSource sources moduleName
+ case s of
+ Left s -> putStrLn ("HINT error: " ++ show s) >> return Nothing
+ Right mod -> do
+ putStrLn mod
+ res <- ghcBuild libFile mod ("Language.Ruby.Hubris.Exports." ++ moduleName) sources defaultGHCOptions
+
+ return (if res
+ then Just libFile
+ else Nothing)
+
+generateSource :: [Filename] -> -- optional haskell source to load into the interpreter
+ ModuleName -> -- name of the module to build a wrapper for
+ IO (Either InterpreterError String)
+generateSource sources moduleName = runInterpreter $ do
+ say $ show sources
+ loadModules sources
+ say "loaded"
+ -- setTopLevelModules [moduleName]
+ setImportsQ $ map (\x->(x,Just x)) $ ("Language.Ruby.Hubris"):("Language.Ruby.Hubris.Binding"):moduleName:[]
+
+
+ functions <- getFunctions moduleName
+ say $ "Candidates: " ++ (show functions)
+ -- ok, let's see if we can come up with an expression of the right type
+
+
+ exportable <- filterM (\func -> do let rubyVal ="(fromIntegral $ fromEnum $ Language.Ruby.Hubris.Binding.RUBY_Qtrue)"
+ let f = "Language.Ruby.Hubris.wrap " ++ moduleName ++"." ++func ++" " ++ rubyVal
+ say f
+ (typeOf f >>= \n -> say $ "type of wrap." ++ func ++ " is " ++ show n)
+ `catchError` (say . show)
+ typeChecks (f ++ "==" ++ rubyVal )) functions
+
+ say $ "Exportable: " ++ (show exportable)
+ -- withTypes <- mapM (\x -> typeOf x >>= \t -> return (x,t)) exportable
+ return $ unlines $
+ -- ideally, all this stuff would be using something like the Interpolator module,
+ -- but haskell-src-meta is not going on 6.12 yet, so let's do it traditionally instead.
+
+ ["{-# LANGUAGE ForeignFunctionInterface #-}",
+ "module Language.Ruby.Hubris.Exports." ++ moduleName ++ " where",
+ "import Language.Ruby.Hubris",
+ "import qualified Prelude",
+ "import Language.Ruby.Hubris.Binding",
+ "import qualified " ++ moduleName] ++
+ [fun ++ " :: Value -> Value" | fun <- exportable ] ++
+ [fun ++ " = Language.Ruby.Hubris.wrap " ++ moduleName ++ "." ++ fun | fun <- exportable ] ++
+
+ ["foreign export ccall \"" ++ fun ++ "\" " ++ fun ++ " :: Value -> Value" | fun <- exportable ]
+
+say = liftIO . putStrLn
+
+getFunctions moduleName = do
+ exports <- getModuleExports moduleName
+ return $ map (\(Fun f) -> f) $ filter isFun exports
+
+isFun (Fun f) = True
+isFun _ = False
View
67 Language/Ruby/Hubris/ZCode.hs
@@ -0,0 +1,67 @@
+{-# LANGUAGE PatternGuards #-}
+
+module Language.Ruby.Hubris.ZCode (zenc,zdec) where
+
+import Data.Char
+import Data.Ix
+import qualified Data.Map as M
+import Numeric
+
+zemap :: M.Map Char String
+zemap = M.fromList $
+ [ ('(', "ZL")
+ , (')', "ZR")
+ , ('[', "ZM")
+ , (']', "ZN")
+ , (':', "ZC")
+ , ('Z', "ZZ")
+
+ , ('z', "zz")
+ , ('&', "za")
+ , ('|', "zb")
+ , ('^', "zc")
+ , ('$', "zd")
+ , ('=', "ze")
+ , ('>', "zg")
+ , ('#', "zh")
+ , ('.', "zi")
+ , ('<', "zl")
+ , ('-', "zm")
+ , ('!', "zn")
+ , ('+', "zp")
+ , ('\'', "zq")
+ , ('\\', "zr")
+ , ('/', "zs")
+ , ('*', "zt")
+ , ('_', "zu")
+ , ('%', "zv")
+ ]
+
+zdmap :: M.Map String Char
+zdmap = M.fromList . map (\(a, b) -> (b, a)) . M.toList $ zemap
+
+zenc, zdec :: String -> String
+
+zenc = concatMap (\c -> M.findWithDefault (z c) c zemap)
+ where
+ z c
+ | any (($ c) . inRange) [('a', 'y'), ('A', 'Z'), ('0', '9')] =
+ [c]
+ | otherwise =
+ let
+ s = showHex (ord c) "U"
+ p = if inRange ('0', '9') (head s) then id else ('0' :)
+ in
+ 'z' : p s
+
+zdec "" = ""
+zdec [c] = [c]
+zdec (c : cs@(c' : cs'))
+ | c `elem` "zZ"
+ , Just x <- M.lookup [c, c'] zdmap
+ = x : zdec cs'
+ | c == 'z'
+ , (h@(_ : _), 'U' : t) <- span isHexDigit cs
+ , [(n, "")] <- readHex h
+ = chr n : zdec t
+ | otherwise = c : zdec cs
View
32 Language/Ruby/Wrappers.hs
@@ -0,0 +1,32 @@
+module Wrappers where
+import Hubris
+data RValue = T_FIXNUM Int
+ | T_STRING String
+ | T_NIL
+ | T_BIGNUM Integer
+ deriving (Eq, Show,Ord)
+
+wrap :: (Haskellable a, Rubyable b) => (a->b) -> (RValue -> RValue)
+wrap func ar = case (toHaskell ar) of
+ Just a -> toRuby $ func a
+ Nothing -> T_NIL
+
+class Haskellable a where
+ toHaskell :: RValue -> Maybe a
+
+class Rubyable a where
+ toRuby :: a -> RValue
+
+instance Haskellable Int where
+ toHaskell (T_FIXNUM i) = Just i
+ toHaskell _ = Nothing
+
+instance Rubyable Int where
+ toRuby i = T_FIXNUM i
+
+instance Haskellable Integer where
+ toHaskell (T_BIGNUM i) = Just i
+ toHaskell _ = Nothing
+
+instance Rubyable Integer where
+ toRuby i = T_BIGNUM i
View
9 Language/Ruby/testLib.hs
@@ -0,0 +1,9 @@
+import Language.Ruby.Hubris.LibraryBuilder
+
+main = do
+ -- Hubris.hs ought to be installed on the system, really.
+ source <- generateSource ["Language/Ruby/Hubris.hs","Language/Ruby/Foo.hs"] "Foo"
+ case source of
+ Left err -> error $ show err
+ Right Nothing -> error "shouldn't happen" -- maybe throw an error in the interpreter monad instead
+ Right (Just a) -> putStrLn a
View
21 Manifest.txt
@@ -1,21 +0,0 @@
-HISTORY.markdown
-Manifest.txt
-PostInstall.txt
-README.markdown
-Rakefile
-bin/jhc_builder
-bin/ghc_builder
-bin-scripts/jhc_builder
-bin-scripts/ghc_builder
-lib/hubris.rb
-lib/rshim.c
-lib/rshim.h
-lib/RubyMap.hs
-lib/RubyMap.chs
-lib/RubyMap.chs.h
-lib/hubris_constants.rb
-sample/Makefile
-sample/Test.hs
-sample/hsload.rb
-spec/spec.opts
-spec/spec_helper.rb
View
3 PostInstall.txt
@@ -1,3 +0,0 @@
-For more information on Hubris, see http://github.com/mwotton/Hubris/tree/master
-
-You should now have a bin file named jhc_builder that you can use to compile Haskell files into .so files
View
53 Rakefile
@@ -1,53 +0,0 @@
-require 'rubygems'
-gem 'hoe', '>= 2.1.0'
-require 'hoe'
-require 'fileutils'
-require './lib/hubris'
-
-# Hoe.plugin :newgem
-# Hoe.plugin :website
-# Hoe.plugin :cucumberfeatures
-
-# Generate all the Rake tasks
-# Run 'rake -T' to see list of generated tasks (from gem root directory)
-$hoe = Hoe.spec 'hubris' do
- self.developer 'Mark Wotton', 'mwotton@gmail.com'
- self.rubyforge_name = "hubris"
- self.summary = 'tool to help build .so files from haskell code for use in Ruby via dl'
- self.post_install_message = 'PostInstall.txt'
- self.readme_file = "README.markdown"
- self.history_file = "HISTORY.markdown"
-end
-
-#require 'newgem/tasks'
-# Dir['tasks/**/*.rake'].each { |t| load t }
-
-
-file "lib/RubyMap.hs" => ["lib/RubyMap.chs"] do
- str = "c2hs -v --cppopts='-I" + Hubris::RubyHeader + "' --cpp=gcc --cppopts=-E --cppopts=-xc lib/RubyMap.chs"
- # print str
- system(str)
-end
-
-require 'spec'
-require 'spec/rake/spectask'
-
-# desc "Run the specs under spec/"
-# all_examples = Spec::Rake::SpecTask.new do |t|
-# t.spec_opts = ['--options', "spec/spec.opts"]
-# t.spec_files = FileList['spec/*.rb']
-# end
-
-task :spec => ["lib/RubyMap.hs"]
-
-task :clean do
- FileList[File.expand_path("~/.hubris_cache/*"), 'lib/*.hi', 'lib/*.ho', 'lib/RubyMap.chs.h', 'lib/RubyMap.chi','lib/RubyMap.hs',
- 'hs.out', 'stubs.c.*', 'hs.out_code*', 'rshim.c*', 'lib*.so', 'lib/*.o', 'libfoo_*.bundle', 'lib/hs.out_code.c' ].each do |f|
- begin
- File.delete(f)
- rescue
- end
- end
- system "cd sample; make clean"
-
-end
View
2 Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
View
29 bin-scripts/ghc_builder
@@ -1,29 +0,0 @@
-
-
-warn "Have ARGV #{ARGV.inspect}"
-
-sh = %~
-#!/bin/sh
-
-# $1 is our source haskell
-rm -rf tmp.old
-mv tmp tmp.old
-mkdir tmp
-tmp="tmp/$1"
-cat $1 >> $tmp
-echo "main :: IO ()" >> $tmp
-echo "main = return ()" >> $tmp
-
-cd tmp
-jhc -dc "$1"
-sed -i 's/^main(/disregard_main(/' hs.out_code.c
-
-# for linux
-gcc '-std=gnu99' -D_GNU_SOURCE '-falign-functions=4' -ffast-math\
- -Wshadow -Wextra -Wall -Wno-unused-parameter -o libdynhs.so\
- hs.out_code.c -DNDEBUG -O3 -fPIC -shared '-D_JHC_STANDALONE=0'
-# for mac
-#gcc '-std=gnu99' -D_GNU_SOURCE '-falign-functions=4' -ffast-math\
-# -Wshadow -Wextra -Wall -Wno-unused-parameter -o libdynhs.so\
-# hs.out_code.c -DNDEBUG -O3 -fPIC -shared '-D_JHC_STANDALONE=0' -dynamiclib
-#mv libdynhs.so ..
View
24 bin-scripts/jhc_builder
@@ -1,24 +0,0 @@
-#!/bin/sh
-
-# $1 is our source haskell
-rm -rf tmp.old
-mv tmp tmp.old
-mkdir tmp
-tmp="tmp/$1"
-cat $1 >> $tmp
-echo "main :: IO ()" >> $tmp
-echo "main = return ()" >> $tmp
-
-cd tmp
-jhc -dc "$1"
-sed -i 's/^main(/disregard_main(/' hs.out_code.c
-
-# for linux
-gcc '-std=gnu99' -D_GNU_SOURCE '-falign-functions=4' -ffast-math\
- -Wshadow -Wextra -Wall -Wno-unused-parameter -o libdynhs.so\
- hs.out_code.c -DNDEBUG -O3 -fPIC -shared '-D_JHC_STANDALONE=0'
- for mac
-#gcc '-std=gnu99' -D_GNU_SOURCE '-falign-functions=4' -ffast-math\
-# -Wshadow -Wextra -Wall -Wno-unused-parameter -o libdynhs.so\
-# hs.out_code.c -DNDEBUG -O3 -fPIC -shared '-D_JHC_STANDALONE=0' -dynamiclib
-#mv libdynhs.so ..
View
4 lib/rshim.c → cbits/rshim.c
@@ -28,3 +28,7 @@ double num2dbl(VALUE x) {
unsigned int rb_ary_len(VALUE x) {
return RARRAY_LEN(x);
}
+
+VALUE keys(VALUE hash) {
+ rb_funcall(hash, rb_intern("keys"), 0);
+}
View
2 lib/rshim.h → cbits/rshim.h
@@ -9,7 +9,7 @@ VALUE int2fix(int i);
int fix2int(VALUE x);
double num2dbl(VALUE x);
unsigned int rb_ary_len(VALUE x);
-
+VALUE keys(VALUE hash);
// argh, and again
enum RubyType {
RT_NONE = T_NONE,
View
0 CommonErrors.txt → doc/CommonErrors.txt
File renamed without changes.
View
25 doc/don_feedback.txt
@@ -0,0 +1,25 @@
+ ------------------------------------------------------------------------
+
+ Hubris
+ Ruby calling Haskell
+ - inline, dynamically compiled Haskell
+ - call precompiled stuff
+
+ Dream
+ Haskell to code would be Cabal package.
+ Ruby would be application.
+
+ FFI preprocessor:
+ Does Haskell foreign export
+ Generates marshalling boilerplate
+
+ Haskell code in Ruby
+
+ ------------------------------------------------------------------------
+
+ Grammar, parsing, type-driven.
+
+ Killer demo:
+ - Rails site,
+ - super clever/ fast/ multicore Haskell code
+
View
17 new_interface.rb → doc/new_interface.rb
@@ -45,12 +45,13 @@ class MyClass
hubris :module => "haskell/shit/MyModule"
# simpler to implement option, allows more flexibility in Ruby land
-module Hubris
- module Data
- module Map
- hubris :package => "containers", :module => "Data.Map"
- end
- end
+module MyRubyModule
+ # :packages is optional, just brings in external packages.
+ hubris :module => "Data.Map", :packages => ["containers","foo"]
+ # or
+ hubris :source => "MyHaskellCode.hs" # , :packages => [ ... ]
+ # or
+ hubris :inline => "foo x = x * 2"
end
@@ -67,3 +68,7 @@ module MyRubyModule
module MyRubyModule
hubris :package => "containers", :module => "Data.Map"
end
+
+
+6:34:58 PM Josh Price: class Module; def hubris; self.class_eval { def self.h;"hubrified!";end };end;end
+6:35:10 PM Josh Price: class B;hubris;end
View
0 hubris.tex → doc/ruby-hubris.tex
File renamed without changes.
View
55 doc/wisdom_of_ancients.txt
@@ -0,0 +1,55 @@
+15:31 < mmorrow> blackdog: i think it's a matter of each .so having the correct list of .so's that its dependent on included within it,
+ which (i'm not positive why) ghc doesn't currently do/have-done
+15:31 -!- Saizan [n=saizan@host19-23-dynamic.9-79-r.retail.telecomitalia.it] has quit [Read error: 110 (Connection timed out)]
+15:32 < mmorrow> blackdog: so i think if you can manage to add this info to your .so files, you'd be ok. i'm not sure about what the
+ options are to accomplish this though (maybe ld has some options to modify .so's)
+15:32 < mmorrow> i don't recall the name of the field for ELF, but iirc it's DEPENDS or something like that
+15:35 < blackdog> so if i want to depend on a standard installed package, i'd have to fiddle with the .so files that are being built so
+ it knows its dependencies too?
+15:36 < blackdog> i think i need .hi files as well - i need to do some reflection on the interface
+15:39 -!- Saizan [n=saizan@host86-241-static.12-87-b.business.telecomitalia.it] has joined #ghc
+15:48 -!- blackh [n=blackh@125-236-232-55.adsl.xtra.co.nz] has quit [Read error: 110 (Connection timed out)]
+15:50 -!- blackh [n=blackh@125-236-232-55.adsl.xtra.co.nz] has joined #ghc
+15:54 -!- Saizan_ [n=saizan@host241-26-dynamic.9-79-r.retail.telecomitalia.it] has quit [Read error: 110 (Connection timed out)]
+16:37 -!- dolio [n=dolio@nr22-66-161-253-70.fuse.net] has quit [Read error: 104 (Connection reset by peer)]
+16:41 -!- dolio [n=dolio@nr22-66-161-253-70.fuse.net] has joined #ghc
+16:46 < mmorrow> blackdog: yeah, fixing the .so files to have the dependency info they should have would fix the linking problem, but
+ if you don't know anything about the code you're dlopen()'ing (and you wanted/needed to), you'd need to decode .hi
+ files
+16:46 < mmorrow> blackdog: which you'd need to use the ghc-api for
+16:47 < mmorrow> blackdog: the old hs-plugins code that had its own .hi parser is bitrotted
+16:47 < mmorrow> since the binary format for .hi files is unstable
+16:50 < mmorrow> blackdog: although i suppose you could "extract" from ghc what you'd need to decode .hi files, and the associated data
+ decls for the contents thereof/etc to avert linking ghc into you app (that'd be nice if the ghc-api itself was in a
+ .so..)
+16:54 -!- Saizan_ [n=saizan@host27-5-dynamic.9-79-r.retail.telecomitalia.it] has joined #ghc
+17:07 < blackdog> mmorrow: thanks, that's useful to know. i won't rely wholly on hs-plugins, then
+17:09 < blackdog> so if I want to be able to do it without an installed ghc, I'd better build a .so myself that can do whatever munging
+ I need, then just embed the dependencies into that and just ship .so files
+17:10 -!- Saizan [n=saizan@host86-241-static.12-87-b.business.telecomitalia.it] has quit [Read error: 110 (Connection timed out)]
+17:10 < blackdog> what would be the semantics of dlopen-ing two haskell .so files and calling them concurrently from C? will horrible
+ things happen?
+17:11 < mmorrow> oh, calling them from C.. hmm, i'm not positive.
+17:12 < mmorrow> i know each module has a module init area, where there a .long/.quad in .data that's either 0 or 1, and initializing a
+ module amounts to jumping to some label in that area, and a module will recursively initialize all mods it depends on
+17:13 < mmorrow> i'm not sure exactly though what the deal is if you've got an rts instance, say, then you dlopen a few haskell .so's
+ from C
+17:14 < blackdog> do you know who would know? it's a difficult thing to test for, it might work fine for a million tests then launch
+ the missiles on the next
+17:14 < mmorrow> blackdog: re: building an .so yourself, aside from ensuring there's the needed .so dep info in the dynamic section (or
+ whatever), i guess you could go about embedding type/etc info in two ways/at two different levels
+17:15 < mmorrow> the first level being to add that info to each module (say in "interface :: Map String Info"), or alternatively (and
+ probably epic) would be to embed that info in the .so file itself somewhere
+17:15 < mmorrow> e.g. ELF has NOTE sections..
+17:15 < blackdog> i think i can probably get away with a bit less information
+17:15 < mmorrow> but that would mean you'd need to parse the object files themselves ..
+17:16 < mmorrow> blackdog: i'd ask JaffaCake about this
+17:16 < mmorrow> if anyone knew what you need to do/what could do wrong i'd say it'd be him
+17:17 -!- bos [n=bos@67.188.108.77] has quit [Read error: 145 (Connection timed out)]
+17:18 < blackdog> well, i can write a program that uses the ghc-api at compile time to get a list of the identifiers in a given module,
+ with their types. I can then filter out the identifiers that don't fit my scheme, and create an SO with the minimal
+ info i need to call it from outside
+17:18 < blackdog> at that stage, the types will be exactly the same, all i need is the names of the wrapper functions
+17:18 < mmorrow> blackdog: totally, that sounds do-able
+17:18 < mmorrow> right
+17:18 < blackdog> ok, cool. thanks for helping me vet my logic:)
View
46 hubris.cabal
@@ -0,0 +1,46 @@
+Name: hubris
+Version: 0.0.1
+Author: Mark Wotton
+Maintainer: mwotton@gmail.com
+Build-Type: Simple
+Cabal-Version: >=1.2
+License: OtherLicense
+License-File: LICENSE
+Build-Type: Simple
+Author: Mark Wotton <mwotton@gmail.com>
+Maintainer: Mark Wotton <mwotton@gmail.com>
+bug-reports: mailto:mwotton@gmail.com
+Category: Language
+Stability: Experimental
+extra-source-files:
+Synopsis: Support library for Hubris, the Ruby <=> Haskell bridge
+Description: Support library for Hubris, the Ruby <=> Haskell bridge
+
+Library
+-- the ordering is critical, because Cabal doesn't do dependency analysis.
+ Exposed-Modules: Language.Ruby.Hubris.Binding, Language.Ruby.Hubris, Language.Ruby.Hubris.LibraryBuilder, Language.Ruby.Hubris.ZCode, Language.Ruby.Hubris.GHCBuild
+-- so this is a really filthy hack.
+ c-sources: cbits/rshim.c
+ includes: cbits/rshim.h
+ install-includes: cbits/rshim.h
+
+-- a proper fix for this would involve autoconf and I'm not feeling up to it.
+ include-dirs: cbits /opt/local/include/ruby-1.9.1/
+ extra-lib-dirs: /opt/local/lib
+ -- c2hsoptions: --cpp=gcc --cppopts=-E --cppopts=-xc
+ extra-libraries: ruby
+ build-depends: ghc, Cabal>=1.7.4 && < 1.8, base, haskell98, containers, bytestring, array, mtl, old-time, ghc-paths, haskell-src-meta, hint
+
+Executable Hubrify
+ Main-is: Hubrify.hs
+ Build-Depends: base >= 3 && < 5, ghc, Cabal>=1.7.4 && < 1.8, base, haskell98, containers, bytestring, array, mtl, old-time, ghc-paths, haskell-src-meta, hint
+ Other-Modules: Language.Ruby.Hubris.Binding
+ c-sources: cbits/rshim.c
+ include-dirs: cbits /opt/local/include/ruby-1.9.1/
+ extra-lib-dirs: /opt/local/lib
+ extra-libraries: ruby
+ -- This is bad form, apparently, and if i include it, ./Setup dist cries big fat tears,
+ -- but you _really_ want a dynamic lib with Hubrify, or you'll get a truly
+ -- huge binary (may not even link, I had problems with the iconv dependency from HSbase)
+ -- anyway, pass "--ghc-options=-dynamic" to ./Setup build, and you should be apples.
+ -- ghc-options: -dynamic
View
22 hubris.gemspec
@@ -1,22 +0,0 @@
-Gem::Specification.new do |s|
- s.name = %q{hubris}
- s.version = "0.0.4"
-
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Mark Wotton", "James Britt", "Josh Price"]
- s.date = %q{2009-08-16}
- s.description = %q{A Ruby Haskell bridge}
- s.email = %q{mwotton@gmail.com}
- s.extra_rdoc_files = ["README.markdown"]
- s.files = ["bin/jhc_builder", "HISTORY.markdown", "lib/extconf.rb", "lib/hubris.rb", "lib/rshim.c", "lib/rshim.h", "lib/RubyMap.chs", "Rakefile", "README.markdown", "sample/hsload.rb", "sample/Makefile", "sample/Test.hs", "spec/basic_ops.rb", "spec/language.rb", "spec/spec.opts", "spec/spec_helper.rb"]
- s.has_rdoc = false
- s.homepage = %q{http://github.com/mwotton/hubris}
- s.rdoc_options = ["--inline-source", "--charset=UTF-8"]
- s.require_paths = ["lib"]
- # s.rubyforge_project = %q{hubris}
- s.rubygems_version = %q{1.3.0}
- s.summary = %q{Hubris is a Ruby Haskell bridge allowing you to call Haskell functions from your Ruby code.}
- %w{rspec open4}.each do |gem|
- s.add_dependency(gem)
- end
-end
View
BIN hubris.pdf
Binary file not shown.
View
181 lib/Makefile
@@ -1,181 +0,0 @@
-
-SHELL = /bin/sh
-
-#### Start of system configuration section. ####
-
-srcdir = .
-topdir = /opt/local/include/ruby-1.9.1
-hdrdir = /opt/local/include/ruby-1.9.1
-arch_hdrdir = /opt/local/include/ruby-1.9.1/$(arch)
-VPATH = $(srcdir):$(arch_hdrdir)/ruby:$(hdrdir)/ruby
-prefix = $(DESTDIR)/opt/local
-exec_prefix = $(prefix)
-vendorhdrdir = $(rubyhdrdir)/vendor_ruby
-sitehdrdir = $(rubyhdrdir)/site_ruby
-rubyhdrdir = $(includedir)/$(RUBY_INSTALL_NAME)-$(ruby_version)
-vendordir = $(libdir)/$(RUBY_INSTALL_NAME)/vendor_ruby
-sitedir = $(libdir)/$(RUBY_INSTALL_NAME)/site_ruby
-mandir = $(DESTDIR)/opt/local/share/man
-localedir = $(datarootdir)/locale
-libdir = $(exec_prefix)/lib
-psdir = $(docdir)
-pdfdir = $(docdir)
-dvidir = $(docdir)
-htmldir = $(docdir)
-infodir = $(datarootdir)/info
-docdir = $(datarootdir)/doc/$(PACKAGE)
-oldincludedir = $(DESTDIR)/usr/include
-includedir = $(prefix)/include
-localstatedir = $(prefix)/var
-sharedstatedir = $(prefix)/com
-sysconfdir = $(prefix)/etc
-datadir = $(datarootdir)
-datarootdir = $(prefix)/share
-libexecdir = $(exec_prefix)/libexec
-sbindir = $(exec_prefix)/sbin
-bindir = $(exec_prefix)/bin
-rubylibdir = $(libdir)/$(ruby_install_name)/$(ruby_version)
-archdir = $(rubylibdir)/$(arch)
-sitelibdir = $(sitedir)/$(ruby_version)
-sitearchdir = $(sitelibdir)/$(sitearch)
-vendorlibdir = $(vendordir)/$(ruby_version)
-vendorarchdir = $(vendorlibdir)/$(sitearch)
-
-CC = /usr/bin/gcc-4.0
-CXX = /usr/bin/g++-4.0
-LIBRUBY = $(LIBRUBY_SO)
-LIBRUBY_A = lib$(RUBY_SO_NAME)-static.a
-LIBRUBYARG_SHARED = -l$(RUBY_SO_NAME)
-LIBRUBYARG_STATIC = -l$(RUBY_SO_NAME)-static
-OUTFLAG = -o
-COUTFLAG = -o
-
-RUBY_EXTCONF_H =
-cflags = $(optflags) $(debugflags) $(warnflags)
-optflags = -O2
-debugflags = -g
-warnflags = -Wall -Wno-parentheses
-CFLAGS = -fno-common -O2 $(cflags) -fno-common -pipe -fno-common
-INCFLAGS = -I. -I$(arch_hdrdir) -I$(hdrdir)/ruby/backward -I$(hdrdir) -I$(srcdir)
-DEFS =
-CPPFLAGS = -I/opt/local/include -D_XOPEN_SOURCE -D_DARWIN_C_SOURCE $(DEFS) $(cppflags)
-CXXFLAGS = $(CFLAGS) -O2 $(cxxflags)
-ldflags = -L. -L/opt/local/lib -L/usr/local/lib
-dldflags =
-archflag =
-DLDFLAGS = $(ldflags) $(dldflags) $(archflag)
-LDSHARED = cc -dynamic -bundle -undefined suppress -flat_namespace
-LDSHAREDXX = $(LDSHARED)
-AR = ar
-EXEEXT =
-
-RUBY_INSTALL_NAME = ruby
-RUBY_SO_NAME = ruby
-arch = i386-darwin9
-sitearch = i386-darwin9
-ruby_version = 1.9.1
-ruby = /opt/local/bin/ruby
-RUBY = $(ruby)
-RM = rm -f
-RM_RF = $(RUBY) -run -e rm -- -rf
-RMDIRS = $(RUBY) -run -e rmdir -- -p
-MAKEDIRS = mkdir -p
-INSTALL = /usr/bin/install -c
-INSTALL_PROG = $(INSTALL) -m 0755
-INSTALL_DATA = $(INSTALL) -m 644
-COPY = cp
-
-#### End of system configuration section. ####
-
-preload =
-
-libpath = . $(libdir)
-LIBPATH = -L. -L$(libdir)
-DEFFILE =
-
-CLEANFILES = mkmf.log
-DISTCLEANFILES =
-DISTCLEANDIRS =
-
-extout =
-extout_prefix =
-target_prefix =
-LOCAL_LIBS =
-LIBS = $(LIBRUBYARG_SHARED) -lpthread -ldl -lobjc
-SRCS = rshim.c
-OBJS = rshim.o
-TARGET = rshim
-DLLIB = $(TARGET).bundle
-EXTSTATIC =
-STATIC_LIB =
-
-BINDIR = $(bindir)
-RUBYCOMMONDIR = $(sitedir)$(target_prefix)
-RUBYLIBDIR = $(sitelibdir)$(target_prefix)
-RUBYARCHDIR = $(sitearchdir)$(target_prefix)
-HDRDIR = $(rubyhdrdir)/ruby$(target_prefix)
-ARCHHDRDIR = $(rubyhdrdir)/$(arch)/ruby$(target_prefix)
-
-TARGET_SO = $(DLLIB)
-CLEANLIBS = $(TARGET).bundle
-CLEANOBJS = *.o *.bak
-
-all: $(DLLIB)
-static: $(STATIC_LIB)
-
-clean-rb-default::
-clean-rb::
-clean-so::
-clean: clean-so clean-rb-default clean-rb
- @-$(RM) $(CLEANLIBS) $(CLEANOBJS) $(CLEANFILES)
-
-distclean-rb-default::
-distclean-rb::
-distclean-so::
-distclean: clean distclean-so distclean-rb-default distclean-rb
- @-$(RM) Makefile $(RUBY_EXTCONF_H) conftest.* mkmf.log
- @-$(RM) core ruby$(EXEEXT) *~ $(DISTCLEANFILES)
- @-$(RMDIRS) $(DISTCLEANDIRS)
-
-realclean: distclean
-install: install-so install-rb
-
-install-so: $(RUBYARCHDIR)
-install-so: $(RUBYARCHDIR)/$(DLLIB)
-$(RUBYARCHDIR)/$(DLLIB): $(DLLIB)
- $(INSTALL_PROG) $(DLLIB) $(RUBYARCHDIR)
-install-rb: pre-install-rb install-rb-default
-install-rb-default: pre-install-rb-default
-pre-install-rb: Makefile
-pre-install-rb-default: Makefile
-$(RUBYARCHDIR):
- $(MAKEDIRS) $@
-
-site-install: site-install-so site-install-rb
-site-install-so: install-so
-site-install-rb: install-rb
-
-.SUFFIXES: .c .m .cc .cxx .cpp .C .o
-
-.cc.o:
- $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
-
-.cxx.o:
- $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
-
-.cpp.o:
- $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
-
-.C.o:
- $(CXX) $(INCFLAGS) $(CPPFLAGS) $(CXXFLAGS) $(COUTFLAG)$@ -c $<
-
-.c.o:
- $(CC) $(INCFLAGS) $(CPPFLAGS) $(CFLAGS) $(COUTFLAG)$@ -c $<
-
-$(DLLIB): $(OBJS) Makefile
- @-$(RM) $(@)
- $(LDSHARED) -o $@ $(OBJS) $(LIBPATH) $(DLDFLAGS) $(LOCAL_LIBS) $(LIBS)
-
-
-
-$(OBJS): $(hdrdir)/ruby.h $(hdrdir)/ruby/defines.h $(arch_hdrdir)/ruby/config.h
View
296 lib/hubris.rb
@@ -1,296 +0,0 @@
-require 'tmpdir'
-require 'rubygems'
-require 'open4'
-require 'digest/md5'
-require 'rbconfig'
-# require 'libHaskell'
-# TODO delete old files
-
-class HaskellError < RuntimeError
-end
-
-def ruby_header_dir
- # Possible config values for 1.8.6:
- # archdir and topdir
- # For 1.9: rubyhdrdir
- Config::CONFIG['rubyhdrdir'] || Config::CONFIG['topdir']
-end
-
-module Hubris
- VERSION = '0.0.2'
- SO_CACHE = File.expand_path("~/.hubris_cache")
-
- system('mkdir ' + SO_CACHE)
- $:.push(SO_CACHE)
-
- def Hubris::find_suitable_ghc()
- # if HUBRIS_GHC is specified, don't try anything else.
- ghcs = ENV['HUBRIS_GHC'] || Dir.glob(ENV['PATH'].split(':').map {|p| p + "/ghc*" }).select {|x| x =~ /\/ghc(-[0-9\.]*)?$/}
- ghcs = ghcs.each { |candidate|
- version = `#{candidate} --numeric-version`.chomp
- return [candidate, version] if version >= '6.11'
- }
- raise(HaskellError, "Can't find an appropriate ghc: tried #{ghcs}")
- end
-
- GHC,GHC_VERSION = Hubris::find_suitable_ghc
- RubyHeader = ruby_header_dir or raise HaskellError, "Can't get rubyhdrdir"
-
- # TODO add foreign export calls immediately for each toplevel func
- # cheap hacky way: first word on each line, nub it to get rid of
- # function types.
- # tricky bit: generating interface for each
- def extract_function_names(haskell_str)
- functions = {}
- haskell_str.each_line do |line|
- # skkeeeeeeetchy. FIXME use haskell-src-exts or something more sensible here
- # ok, so now we have ExtractHeaders. Can't really integrate it using Hubris,
- # for obvious reasons, but we can build an extension
- if /^[^ \-{].*/ =~ line
- functions[line.split(/ /)[0]] = 1
- end
- end
- functions.keys
- end
-
- def make_haskell_bindings(functions)
- prelude =<<-EOF
-{-# LANGUAGE ScopedTypeVariables, FlexibleInstances, ForeignFunctionInterface, UndecidableInstances #-}
--- import Foreign.Ptr()
-import RubyMap
-import Prelude hiding (catch)
-import Control.Exception(SomeException, evaluate, catch)
-import Foreign(unsafePerformIO)
-main :: IO ()
-main = return ()
-
-EOF
- bindings = ""
- # cheap way: assert type sigs binding to RValue. Might be able to do better after,
- # but this'll do for the moment
- functions.each do |fname|
- bindings +=<<-EOF
-#{fname} :: RValue -> RValue
-#{fname}_external :: Value -> Value -> Value
-#{fname}_external _mod x = unsafePerformIO $
- (evaluate (toRuby $ #{fname} $ fromRuby x))
- `catch` (\\y -> throwException (show (y::SomeException)))
-foreign export ccall "#{fname}_external" #{fname}_external :: Value -> Value -> Value
-
- EOF
- end
- return prelude + bindings
- end
-
- def trans_name(func)
- func.gsub(/Z/, 'ZZ').gsub(/z/, 'zz').gsub(/\./,'zd').gsub(/_/,'zu').gsub(/'/, 'zq')
- end
-
- def base_loader_code mod_name, lib_name
- %~/* so, here's the story. We have the functions, and we need to expose them to Ruby */
-#include "rshim.h"
-VALUE #{mod_name} = Qnil;
-extern void hs_init(int * argc, char ** argv[]);
-
-void Init_#{lib_name}() {
- int argc = 1;
- // this needs to be allocated on the heap or we get a segfault
- char ** argv = malloc(sizeof(char**) * 1);
- argv[0]="haskell_extension";
- hs_init(&argc, &argv);
- #{mod_name} = rb_define_class("#{mod_name}", rb_cObject);
- ~
-
- end
-
- def make_stub(mod_name, lib_name, functions)
- loader_code = base_loader_code(mod_name, lib_name)
-
- functions.each do |function_name|
- loader_code += "VALUE #{function_name}_external(VALUE);\n"
- # FIXME add the stg roots as well
- # loaderCode += "extern void __stginit_#{function_name}zuexternal(void);\n"
- end
-
- functions.each do |function_name|
- # FIXME this is the worng place to be binding methods. Can we bind a bare C method in Ruby
- # instead?
- loader_code += "rb_define_method(#{mod_name},\"#{function_name}\",#{function_name}_external, 1);\n"
- # FIXME this is needed for GHC
- # loader_code += "hs_add_root(__stginit_#{trans_name(function_name + '_external')});\n"
- end
- return loader_code + "}\n"
- end
-
- def dylib_suffix
- case Config::CONFIG['target_os']
- when /darwin/
- "bundle"
- when /linux/
- "so"
- else
- "so" #take a punt
- end
- end
-
- def builder
- 'ghc'
- end
-
- def base_lib_dir
- File.expand_path( File.dirname(__FILE__))
- end
-
- def inline(haskell_str, build_options={ })
- # this is a bit crap. You wouldn't have to specify the args in an FP language :/
- # should probably switch out to a couple of single-method classes
- # argh
- # """
- # Ruby's lambda is unusual in that choice of parameter names does affect behavior:
- # x = 3
- # lambda{|x| "x still refers to the outer variable"}.call(4)
- # puts x # x is now 4, not 3
- # """
- # this is a solved problem, guys. come ON. FIXME
-
- builders = { "jhc" => lambda { |x,y,z,a| jhcbuild(x,y,z,a) }, "ghc" => lambda { |x,y,z,a| ghcbuild(x,y,z,a) } }
-
- signature = Digest::MD5.hexdigest(haskell_str)
- functions = extract_function_names(haskell_str)
-
- return unless functions.size > 0
-
- lib_name = "lib#{functions[0]}_#{signature}"; # unique signature
- lib_file = SO_CACHE + "/" + lib_name + '.' + dylib_suffix
- file_path = File.join(Dir.tmpdir, functions[0] + "_source.hs")
-
-
- # if the haskell libraries have changed out from under us, that's just too bad.
- # If we've changed details of this script, however, we probably want to rebuild,
- # just to be safe.
- if !File.exists?(lib_file) or File.mtime(__FILE__) >= File.mtime(lib_file)
- mod_name = self.class
- write_hs_file( file_path, haskell_str, functions, mod_name, lib_name )
- File.open("stubs.c", "w") {|io| io.write(make_stub(mod_name, lib_name, functions))}
- # and it all comes together
- build_result = builders[builder].call(lib_file, file_path , ['stubs.c', base_lib_dir + '/rshim.c'], build_options)
-
- end
-
- begin
- require lib_name
- # raise LoadError
- rescue LoadError
- raise LoadError, "loading #{lib_name} failed, source was\n" + `cat #{file_path}` +
- "\n" + $!.to_s + "\n" + `nm #{lib_file} |grep 'ext'` + "\n" +
- (build_result || "no build result?") + "\n"
- end
- end
-
- def write_hs_file file_path, haskell_str, functions, mod_name, lib_name
- File.open( file_path , "w") do |file|
- # so the hashing algorithm doesn't collide if we try building the same code
- # with jhc and ghc.
- #
- # argh, this isn't quite right. If we inline the same code but on a new ruby module
- # this won't create the new stubs. We want to be able to use new stubs but with the
- # old haskell lib. FIXME
- file.print("-- COMPILED WITH #{builder}\n")
- file.print(make_haskell_bindings(functions))
- file.print(haskell_str)
- file.flush
- end
- end
-
- # This is obviously weak, but there are many ways people may have various GHC installations,
- # and the previous assumptions were also bad. Need to decide if a) code should be super clever and
- # detect this sort of thing, or b) user should just set config values someplace. Or c) some hybrid.
- # James likes (b).
- def ghc_build_path
- ENV['HUBRIS_GHC_BUILD_PATH'] || '/usr/local'
- end
-
- def ghcbuild(lib_file, haskell_path, extra_c_src, options)
- # this could be even less awful.
- # ghc-paths fixes this
- command = "#{GHC} -Wall -v --make -dynamic -fPIC -shared #{haskell_path} -lHSrts-ghc#{GHC_VERSION} " +
- "-L#{ghc_build_path}/lib/ghc-#{GHC_VERSION} " +
- "-no-hs-main " +
- # -L/Users/mwotton/projects/ghc \
- "-optl-Wl,-rpath,#{ghc_build_path}/lib/ghc-#{GHC_VERSION} " +
- # "-optl-Wl,-macosx_version_min,10.5 " +
- "-o #{lib_file} " + extra_c_src.join(' ') + ' ' + base_lib_dir + '/RubyMap.hs -I' + Hubris::RubyHeader + ' -I./lib'
- if (not options[:no_strict])
- command += ' -Werror ' # bondage and discipline
- end
- success,msg=noisy(command)
- # puts [success,msg]
- unless success
- raise HaskellError, "ghc build failed " + msg + `cat #{haskell_path}`
- end
- return msg
- end
-
- def jhcbuild(lib_file, haskell_path, extra_c_src)
- noisy("rm hs.out_code.c 2>/dev/null")
- # puts "building\n#{file.read}"
- success, msg = noisy("jhc -dc #{haskell_path} -papplicative -ilib")
- unless success || File.exists?("hs.out_code.c")
- raise HaskellError, "JHC build failed:\nsource\n" + `cat #{haskell_path}` + "\n#{msg}"
- end
- # puts msg
-
- # output goes to hs_out.code.c
- # don't need to grep out main any more
- # we do need to grep out rshim.h, though. why? no one knows. better solution please
- system("echo '#include <rshim.h>' > temp.c;")
- system("grep -v '#include \<rshim.h\>' < hs.out_code.c | sed 's/ALIGN(/JHCS_ALIGN(/g' >> temp.c; mv temp.c hs.out_code.c;")
-
- # FIXME generalise to linux, this is probably Mac only.
- lDFLAGS = [ '-dynamiclib',
- '-fPIC',
- '-shared'
- # '-lruby',
- ]
- mACFLAGS = [
- '-undefined suppress',
- '-flat_namespace'
- ]
- cPPFLAGS = [
- '-D_GNU_SOURCE',
- '-D_JHC_STANDALONE=0',
- '-DNDEBUG'
- ]
- cFLAGS = ['-std=gnu99',
- '-falign-functions=4',
- '-ffast-math',
- '-Wshadow', '-Wextra', '-Wall', '-Wno-unused-parameter',
- "-g -O3 -o #{lib_file}"]
- sRC = [
- './hs.out_code.c'
- ] + extra_c_src
-
- iNCLUDES = ["-I#{RubyHeader}", '-I./lib']
- system "rm #{lib_file} 2>/dev/null"
-
- success, msg = noisy("gcc " + [cPPFLAGS, cFLAGS, lDFLAGS, iNCLUDES, sRC].join(" "))
- puts msg
- unless success
- raise SyntaxError, "C build failed:\n#{msg}"
- end
- end
-end
-
-def noisy(str)
- pid, stdin, stdout, stderr = Open4.popen4 str
- ignored, status = Process.waitpid2 pid
- if status == 0
- [true, str + "\n"]
- else
- msg = <<-"EOF"
-output: #{stdout.read}
-error: #{stderr.read}
- EOF
- [false, str + "\n" + msg]
- end
-end
View
4 lib/hubris_constants.rb
@@ -1,4 +0,0 @@
-module Hubris
- VERSION = '0.0.3'
- SO_CACHE = "~/.hubris_cache"
-end
View
10 script/console
@@ -1,10 +0,0 @@
-#!/usr/bin/env ruby
-# File: script/console
-irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
-
-libs = " -r irb/completion"
-# Perhaps use a console_lib to store any extra methods I may want available in the cosole
-# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
-libs << " -r #{File.dirname(__FILE__) + '/../lib/hubris.rb'}"
-puts "Loading Hubris gem"
-exec "#{irb} #{libs} --simple-prompt"
View
14 script/destroy
@@ -1,14 +0,0 @@
-#!/usr/bin/env ruby
-APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
-
-begin
- require 'rubigen'
-rescue LoadError
- require 'rubygems'
- require 'rubigen'
-end
-require 'rubigen/scripts/destroy'
-
-ARGV.shift if ['--help', '-h'].include?(ARGV[0])
-RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
-RubiGen::Scripts::Destroy.new.run(ARGV)
View
14 script/generate
@@ -1,14 +0,0 @@
-#!/usr/bin/env ruby
-APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
-
-begin
- require 'rubigen'
-rescue LoadError
- require 'rubygems'
- require 'rubigen'
-end
-require 'rubigen/scripts/generate'
-
-ARGV.shift if ['--help', '-h'].include?(ARGV[0])
-RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
-RubiGen::Scripts::Generate.new.run(ARGV)
View
15 spec/hubris_spec.rb
@@ -1,5 +1,7 @@
load File.dirname(__FILE__) + '/spec_helper.rb'
+Hubris.add_packages %w(base bytestring)
+
# # just want to check it's actually possible to load a library dynamically
# describe "dlload" do
# it "actually builds and loads a C level dylib stupidly" do
@@ -8,7 +10,7 @@
# end
# end
-class Target
+module Target
include Hubris
def foo_local
14
@@ -18,9 +20,9 @@ def foo_local
Signal.trap("INT", 'EXIT');
describe "Target" do
- it "whines like a little baby when you pass it bad haskell" do
- t = Target.new
- lambda{ t.inline("broken _ = (1 + \"a string\")")}.should raise_error(HaskellError)
+ it "whines like a little baby when you pass it bad haskell" do
+ # t = Target.new
+ lambda{ module Foo; hubris :inline => "broken _ = (1 + \"a string\")"; end}.should raise_error(HaskellError)
end
it "ignores a comment" do
@@ -30,8 +32,9 @@ def foo_local
end
it "sings like a golden bird when you treat it right, aw yeah" do
- t = Target.new
- lambda { t.inline("working _ = T_FIXNUM (1+2)", { :no_strict => true }) }.should_not raise_error
+# t = Target.new
+# lambda { t.inline("working _ = T_FIXNUM (1+2)", { :no_strict => true }) }.should_not raise_error
+ lambda { Target.inline("working _ = T_FIXNUM (1+2)", { :no_strict => true }) }.should_not raise_error
end
View
8 tasks/compile_sample.rake
@@ -1,8 +0,0 @@
-
-file 'sample/libdynhs.so' => ['sample/Test.hs'] do
- sh "./bin/jhc_builder.sh sample/Test.hs"
-end
-
-#file 'lib/RubyMap.hs' => ['lib/RubyMap.chs'] do
-# sh "cd lib; c2hs -v --cppopts='-I/opt/local/include/ruby-1.9.1/ruby' --cpp=gcc --cppopts=-E --cppopts=-xc RubyMap.chs; cd .."
-#end

0 comments on commit ccf024e

Please sign in to comment.
Something went wrong with that request. Please try again.