hubris 0.0.3 → 0.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +31 -0
- data/.rvmrc +2 -0
- data/Gemfile +11 -0
- data/Haskell/Hubrify.hs +69 -0
- data/Haskell/LICENSE +22 -0
- data/Haskell/Language/Ruby/Foo.hs +20 -0
- data/Haskell/Language/Ruby/Hubris/Binding.hsc +214 -0
- data/Haskell/Language/Ruby/Hubris/GHCBuild.hs +46 -0
- data/Haskell/Language/Ruby/Hubris/Hash.hs +27 -0
- data/Haskell/Language/Ruby/Hubris/Interpolator.hs +22 -0
- data/Haskell/Language/Ruby/Hubris/LibraryBuilder.hs +181 -0
- data/Haskell/Language/Ruby/Hubris/ZCode.hs +68 -0
- data/Haskell/Language/Ruby/Hubris.hs +254 -0
- data/Haskell/Language/Ruby/Wrappers.hs +32 -0
- data/Haskell/Language/Ruby/testLib.hs +9 -0
- data/Haskell/Setup.hs +31 -0
- data/Haskell/cbits/rshim.c +46 -0
- data/Haskell/cbits/rshim.h +50 -0
- data/Haskell/hubris.cabal +53 -0
- data/INSTALL +21 -0
- data/Manifest.txt +22 -0
- data/PostInstall.txt +1 -0
- data/README.markdown +107 -0
- data/Rakefile +46 -43
- data/VERSION +1 -0
- data/doc/CommonErrors.txt +18 -0
- data/doc/CommonErrors.txt~HEAD +18 -0
- data/doc/don_feedback.txt +25 -0
- data/doc/haskell-hubris.tex +242 -0
- data/doc/new_interface.rb +74 -0
- data/doc/ruby-hubris.tex +176 -0
- data/doc/wisdom_of_ancients.txt +55 -0
- data/ext/hubris.rb +4 -0
- data/ext/stub/extconf.rb +5 -0
- data/ext/{HubrisStubLoader.c → stub/stub.c} +1 -1
- data/hubris.gemspec +31 -0
- data/lib/Makefile +181 -0
- data/lib/hubris/version.rb +3 -0
- data/lib/hubris.rb +16 -13
- data/rspec.rake +21 -0
- data/sample/Fibonacci.hs +2 -2
- data/sample/config.ru +3 -1
- data/script/ci.sh +25 -0
- data/script/console +10 -0
- data/spec/hubris_spec.rb +173 -47
- data/tasks/extconf/stub.rake +43 -0
- data/tasks/extconf.rake +13 -0
- metadata +118 -27
- data/ext/extconf.rb +0 -5
data/.gitignore
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
*.aux
|
2
|
+
*.dvi
|
3
|
+
*.gem
|
4
|
+
*.hi
|
5
|
+
*.log
|
6
|
+
*.nav
|
7
|
+
*.o
|
8
|
+
*.out
|
9
|
+
*.snm
|
10
|
+
*.toc
|
11
|
+
*.vrb
|
12
|
+
.#*
|
13
|
+
.bundle
|
14
|
+
.jhci-hist
|
15
|
+
RubyMap.hi
|
16
|
+
RubyMap.hs
|
17
|
+
RubyMap.o
|
18
|
+
Setup
|
19
|
+
dist
|
20
|
+
hs.out
|
21
|
+
hs.out_code.c
|
22
|
+
lib/RubyMap.chi
|
23
|
+
lib/RubyMap.chs.h
|
24
|
+
lib/RubyMap.hi
|
25
|
+
lib/RubyMap.hs
|
26
|
+
lib/RubyMap.o
|
27
|
+
lib/rshim.o
|
28
|
+
pkg/
|
29
|
+
pkg/*
|
30
|
+
sample/tmp
|
31
|
+
sample/tmp.old
|
data/.rvmrc
ADDED
data/Gemfile
ADDED
data/Haskell/Hubrify.hs
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
module Main where
|
2
|
+
import Language.Ruby.Hubris.LibraryBuilder
|
3
|
+
import System
|
4
|
+
import System.Exit
|
5
|
+
-- import Control.Monad (when)
|
6
|
+
import System.Console.GetOpt
|
7
|
+
|
8
|
+
data Options = Options
|
9
|
+
{ optVerbose :: Bool
|
10
|
+
, optStrict :: Bool
|
11
|
+
, optShowVersion :: Bool
|
12
|
+
, optOutput :: FilePath
|
13
|
+
, optModule :: String
|
14
|
+
, optInput :: Maybe FilePath
|
15
|
+
, optPackages :: [String]
|
16
|
+
} deriving Show
|
17
|
+
|
18
|
+
defaultOptions :: Options
|
19
|
+
defaultOptions = Options
|
20
|
+
{ optVerbose = False
|
21
|
+
, optShowVersion = False
|
22
|
+
, optOutput = error "output must be defined"
|
23
|
+
, optModule = error "module must be defined"
|
24
|
+
, optStrict = False
|
25
|
+
, optInput = Nothing
|
26
|
+
, optPackages = []
|
27
|
+
}
|
28
|
+
|
29
|
+
options :: [OptDescr (Options -> Options)]
|
30
|
+
options =
|
31
|
+
[ Option "v" ["verbose"]
|
32
|
+
(NoArg (\opts -> opts { optVerbose = True }))
|
33
|
+
"chatty output on stderr"
|
34
|
+
, Option [] ["strict"]
|
35
|
+
(NoArg (\opts -> opts { optStrict = True }))
|
36
|
+
"bondage and discipline mode"
|
37
|
+
, Option "o" ["output"]
|
38
|
+
(ReqArg (\f opts -> opts { optOutput = f }) "libFile")
|
39
|
+
"output FILE"
|
40
|
+
, Option "m" ["module"]
|
41
|
+
(ReqArg (\f opts -> opts { optModule = f }) "module")
|
42
|
+
"module to be wrapped"
|
43
|
+
, Option "p" ["package"]
|
44
|
+
(ReqArg (\d opts -> opts { optPackages = optPackages opts ++ [d] }) "DIR")
|
45
|
+
"package"
|
46
|
+
]
|
47
|
+
|
48
|
+
hubrisOpts :: [String] -> IO (Options, [String])
|
49
|
+
hubrisOpts argv =
|
50
|
+
case getOpt Permute options argv of
|
51
|
+
(o, n, []) -> return (foldl (flip id) defaultOptions o, n)
|
52
|
+
(_, _, errs) -> ioError (userError (concat errs ++ usageInfo header options))
|
53
|
+
where header = "Usage: Hubrify --module MODULE --output LIBFILE (--packages PACKAGE1 ...) sourceFiles ..."
|
54
|
+
|
55
|
+
main :: IO ()
|
56
|
+
main = do
|
57
|
+
(o, srcs) <- getArgs >>= hubrisOpts
|
58
|
+
-- HACK this may be the worst thing ever
|
59
|
+
|
60
|
+
let ghcArgs = if optStrict o
|
61
|
+
then ["-Wall", "-Werror", "-fno-warn-unused-imports"]
|
62
|
+
else []
|
63
|
+
-- putStrLn $ show $ optPackages o
|
64
|
+
|
65
|
+
res <- generateLib (optOutput o) srcs (optModule o) ("-fPIC":ghcArgs) (optPackages o)
|
66
|
+
-- print res
|
67
|
+
case res of
|
68
|
+
Left a -> putStrLn ("Failed: " ++ a) >> exitFailure
|
69
|
+
Right _ -> exitSuccess
|
data/Haskell/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
(The MIT License)
|
2
|
+
|
3
|
+
Copyright (c) 2009 Mark Wotton
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
'Software'), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
19
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
20
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
21
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
22
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Foo (wrap, testString, wrap2, external, can_be_called, cannot_be_called) where
|
2
|
+
can_be_called :: Int -> Int
|
3
|
+
can_be_called x = 2 * x
|
4
|
+
|
5
|
+
could_be_called :: Int -> Int
|
6
|
+
could_be_called x = 3 * x
|
7
|
+
wrap :: Int -> Int
|
8
|
+
wrap x = 3 - x
|
9
|
+
|
10
|
+
wrap2 :: Int -> Int
|
11
|
+
wrap2 x = 3 - x
|
12
|
+
|
13
|
+
cannot_be_called = map
|
14
|
+
|
15
|
+
internal = 1
|
16
|
+
external = 10
|
17
|
+
|
18
|
+
|
19
|
+
testString :: Int -> String
|
20
|
+
testString x = show x
|
@@ -0,0 +1,214 @@
|
|
1
|
+
{-# LANGUAGE ForeignFunctionInterface, CPP, TypeSynonymInstances #-}
|
2
|
+
|
3
|
+
{- TODO
|
4
|
+
|
5
|
+
Rip the array trnaslation stuff out to a utility function. same with hashes.
|
6
|
+
|
7
|
+
install as package. This is a bit iffy for GHC/JHC compatibility - if we commit to
|
8
|
+
Cabal, that leaves JHC out in the cold.
|
9
|
+
|
10
|
+
perhaps need cabal file for ghc and equivalent for jhc.
|
11
|
+
|
12
|
+
also: do we want to support different versions of ruby? for the moment, you just get
|
13
|
+
whichever ruby.h is first in the search place.
|
14
|
+
|
15
|
+
-}
|
16
|
+
|
17
|
+
|
18
|
+
module Language.Ruby.Hubris.Binding where
|
19
|
+
|
20
|
+
#include "rshim.h"
|
21
|
+
#include <ruby.h>
|
22
|
+
|
23
|
+
import Control.Applicative
|
24
|
+
-- import Control.Monad
|
25
|
+
import Data.Word
|
26
|
+
-- import Foreign.Ptr
|
27
|
+
import Foreign.C.Types
|
28
|
+
import Foreign.C.String
|
29
|
+
import System.IO.Unsafe (unsafePerformIO)
|
30
|
+
import Data.Maybe
|
31
|
+
|
32
|
+
rubyType :: Value -> RubyType
|
33
|
+
rubyType = toEnum . rtype
|
34
|
+
|
35
|
+
-- this is awful, but I don't want to pull in C2HS - it pulls in
|
36
|
+
-- alex and happy, and they're difficult to specify as dependencies
|
37
|
+
-- in cabal.
|
38
|
+
|
39
|
+
#{enum Int,,
|
40
|
+
T_NONE,
|
41
|
+
T_NIL,
|
42
|
+
T_OBJECT,
|
43
|
+
T_CLASS,
|
44
|
+
T_ICLASS,
|
45
|
+
T_MODULE ,
|
46
|
+
T_FLOAT ,
|
47
|
+
T_STRING ,
|
48
|
+
T_REGEXP ,
|
49
|
+
T_ARRAY ,
|
50
|
+
T_FIXNUM ,
|
51
|
+
T_HASH ,
|
52
|
+
T_STRUCT ,
|
53
|
+
T_BIGNUM ,
|
54
|
+
T_FILE ,
|
55
|
+
|
56
|
+
T_TRUE ,
|
57
|
+
T_FALSE ,
|
58
|
+
T_DATA ,
|
59
|
+
T_MATCH ,
|
60
|
+
T_SYMBOL ,
|
61
|
+
|
62
|
+
T_UNDEF ,
|
63
|
+
T_NODE ,
|
64
|
+
T_MASK }
|
65
|
+
|
66
|
+
data RubyType = RT_NONE
|
67
|
+
| RT_NIL
|
68
|
+
| RT_OBJECT
|
69
|
+
| RT_CLASS
|
70
|
+
| RT_ICLASS
|
71
|
+
| RT_MODULE
|
72
|
+
| RT_FLOAT
|
73
|
+
| RT_STRING
|
74
|
+
| RT_REGEXP
|
75
|
+
| RT_ARRAY
|
76
|
+
| RT_FIXNUM
|
77
|
+
| RT_HASH
|
78
|
+
| RT_STRUCT
|
79
|
+
| RT_BIGNUM
|
80
|
+
| RT_FILE
|
81
|
+
|
82
|
+
| RT_TRUE
|
83
|
+
| RT_FALSE
|
84
|
+
| RT_DATA
|
85
|
+
| RT_MATCH
|
86
|
+
| RT_SYMBOL
|
87
|
+
|
88
|
+
| RT_UNDEF
|
89
|
+
| RT_NODE
|
90
|
+
| RT_MASK
|
91
|
+
deriving (Eq, Show)
|
92
|
+
|
93
|
+
instance Enum RubyType where
|
94
|
+
fromEnum RT_NONE = tNone
|
95
|
+
|
96
|
+
fromEnum RT_NIL = tNil
|
97
|
+
fromEnum RT_OBJECT = tObject
|
98
|
+
fromEnum RT_CLASS = tClass
|
99
|
+
fromEnum RT_ICLASS = tIclass
|
100
|
+
fromEnum RT_MODULE = tModule
|
101
|
+
fromEnum RT_FLOAT = tFloat
|
102
|
+
fromEnum RT_STRING = tString
|
103
|
+
fromEnum RT_REGEXP = tRegexp
|
104
|
+
fromEnum RT_ARRAY = tArray
|
105
|
+
fromEnum RT_FIXNUM = tFixnum
|
106
|
+
fromEnum RT_HASH = tHash
|
107
|
+
fromEnum RT_STRUCT = tStruct
|
108
|
+
fromEnum RT_BIGNUM = tBignum
|
109
|
+
fromEnum RT_FILE = tFile
|
110
|
+
|
111
|
+
fromEnum RT_TRUE = tTrue
|
112
|
+
fromEnum RT_FALSE = tFalse
|
113
|
+
fromEnum RT_DATA = tData
|
114
|
+
fromEnum RT_MATCH = tMatch
|
115
|
+
fromEnum RT_SYMBOL = tSymbol
|
116
|
+
fromEnum RT_UNDEF = tUndef
|
117
|
+
fromEnum RT_NODE = tNode
|
118
|
+
|
119
|
+
fromEnum RT_MASK = tMask
|
120
|
+
-- this is unnecessarily slow. fix it later.
|
121
|
+
toEnum x = fromJust $ lookup x assoc
|
122
|
+
where assoc = [( tNone , RT_NONE)
|
123
|
+
,( tNil , RT_NIL )
|
124
|
+
,( tObject , RT_OBJECT )
|
125
|
+
,( tClass , RT_CLASS )
|
126
|
+
,( tIclass , RT_ICLASS )
|
127
|
+
,( tModule , RT_MODULE )
|
128
|
+
,( tFloat , RT_FLOAT )
|
129
|
+
,( tString , RT_STRING )
|
130
|
+
,( tRegexp , RT_REGEXP )
|
131
|
+
,( tArray , RT_ARRAY )
|
132
|
+
,( tFixnum , RT_FIXNUM )
|
133
|
+
,( tHash , RT_HASH )
|
134
|
+
,( tStruct , RT_STRUCT )
|
135
|
+
,( tBignum , RT_BIGNUM )
|
136
|
+
,( tFile , RT_FILE )
|
137
|
+
|
138
|
+
,( tTrue , RT_TRUE )
|
139
|
+
,( tFalse , RT_FALSE )
|
140
|
+
,( tData , RT_DATA )
|
141
|
+
,( tMatch , RT_MATCH )
|
142
|
+
,( tSymbol , RT_SYMBOL )
|
143
|
+
,( tUndef , RT_UNDEF )
|
144
|
+
,( tNode , RT_NODE )
|
145
|
+
|
146
|
+
,( tMask , RT_MASK )]
|
147
|
+
|
148
|
+
|
149
|
+
|
150
|
+
|
151
|
+
constToRuby :: RubyConst -> Value
|
152
|
+
constToRuby = fromIntegral . fromEnum
|
153
|
+
-- RUBY_VERSION_CODE <= 187
|
154
|
+
data RubyConst = RUBY_Qfalse
|
155
|
+
| RUBY_Qtrue
|
156
|
+
| RUBY_Qnil
|
157
|
+
| RUBY_Qundef
|
158
|
+
|
159
|
+
instance Enum RubyConst where
|
160
|
+
fromEnum RUBY_Qfalse = 0
|
161
|
+
fromEnum RUBY_Qtrue = 2
|
162
|
+
fromEnum RUBY_Qnil = 4
|
163
|
+
fromEnum RUBY_Qundef = 6
|
164
|
+
|
165
|
+
toEnum 0 = RUBY_Qfalse
|
166
|
+
toEnum 2 = RUBY_Qtrue
|
167
|
+
toEnum 4 = RUBY_Qnil
|
168
|
+
toEnum 6 = RUBY_Qundef
|
169
|
+
toEnum 12 = RUBY_Qnil
|
170
|
+
-- {# enum ruby_special_consts as RubyConst {} deriving (Eq,Show) #}
|
171
|
+
|
172
|
+
str2cstr str = rb_str2cstr str 0
|
173
|
+
type Value = CULong -- FIXME, we'd prefer to import the type VALUE directly
|
174
|
+
foreign import ccall safe "ruby.h rb_str2cstr" rb_str2cstr :: Value -> CInt -> IO CString
|
175
|
+
foreign import ccall safe "ruby.h rb_str_new2" rb_str_new2 :: CString -> IO Value
|
176
|
+
foreign import ccall safe "ruby.h rb_str_new2" rb_str_new_ :: CString -> Int -> IO Value
|
177
|
+
foreign import ccall safe "ruby.h rb_ary_new2" rb_ary_new2 :: CLong -> IO Value
|
178
|
+
foreign import ccall safe "ruby.h rb_ary_push" rb_ary_push :: Value -> Value -> IO ()
|
179
|
+
foreign import ccall safe "ruby.h rb_float_new" rb_float_new :: Double -> Value
|
180
|
+
foreign import ccall safe "ruby.h rb_big2str" rb_big2str :: Value -> Int -> IO Value
|
181
|
+
foreign import ccall safe "ruby.h rb_str_to_inum" rb_str_to_inum :: Value -> Int -> Int -> Value
|
182
|
+
-- foreign import ccall safe "ruby.h ruby_init" ruby_init :: IO ()
|
183
|
+
|
184
|
+
rb_str_new = uncurry rb_str_new_
|
185
|
+
|
186
|
+
-- we're being a bit filthy here - the interface is all macros, so we're digging in to find what it actually is
|
187
|
+
foreign import ccall safe "rshim.h rb_ary_len" rb_ary_len :: Value -> CUInt
|
188
|
+
foreign import ccall safe "rshim.h rtype" rtype :: Value -> Int
|
189
|
+
|
190
|
+
foreign import ccall safe "rshim.h int2fix" int2fix :: Int -> Value
|
191
|
+
foreign import ccall safe "rshim.h fix2int" fix2int :: Value -> Int
|
192
|
+
foreign import ccall safe "rshim.h num2dbl" num2dbl :: Value -> Double -- technically CDoubles, but jhc promises they're the same
|
193
|
+
foreign import ccall safe "rshim.h keys" rb_keys :: Value -> IO Value
|
194
|
+
foreign import ccall safe "rshim.h buildException" buildException :: CString -> IO Value
|
195
|
+
-- foreign import ccall safe "ruby.h rb_funcall" rb_funcall :: Value -> ID ->
|
196
|
+
|
197
|
+
-- this line crashes jhc
|
198
|
+
foreign import ccall safe "intern.h rb_ary_entry" rb_ary_entry :: Value -> CLong -> IO Value
|
199
|
+
|
200
|
+
foreign import ccall safe "ruby.h rb_raise" rb_raise :: Value -> CString -> IO ()
|
201
|
+
foreign import ccall safe "ruby.h rb_eval_string" rb_eval_string :: CString -> IO Value
|
202
|
+
|
203
|
+
foreign import ccall safe "intern.h rb_hash_aset" rb_hash_aset :: Value -> Value -> Value -> IO ()
|
204
|
+
foreign import ccall safe "intern.h rb_hash_new" rb_hash_new :: IO Value
|
205
|
+
foreign import ccall safe "intern.h rb_hash_aref" rb_hash_aref :: Value -> Value -> IO Value
|
206
|
+
|
207
|
+
|
208
|
+
createException :: String -> IO Value
|
209
|
+
createException s = newCAString s >>= buildException
|
210
|
+
|
211
|
+
|
212
|
+
|
213
|
+
|
214
|
+
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Language.Ruby.Hubris.GHCBuild (ghcBuild, defaultGHCOptions, GHCOptions(..), withTempFile) where
|
2
|
+
import Config
|
3
|
+
import Debug.Trace
|
4
|
+
import DynFlags
|
5
|
+
import GHC
|
6
|
+
import GHC.Paths
|
7
|
+
import Outputable
|
8
|
+
import StringBuffer
|
9
|
+
import System.Process
|
10
|
+
import Control.Monad(forM_,guard)
|
11
|
+
import System.IO(hPutStr, hClose, openTempFile)
|
12
|
+
import System( exitWith, system)
|
13
|
+
import System.Exit
|
14
|
+
import Language.Ruby.Hubris.Includes (extraIncludeDirs) -- this is generated by Cabal
|
15
|
+
|
16
|
+
|
17
|
+
newtype GHCOptions = GHCOptions { strict :: Bool }
|
18
|
+
defaultGHCOptions = GHCOptions { strict = True }
|
19
|
+
type Filename = String
|
20
|
+
|
21
|
+
|
22
|
+
standardGHCFlags = (words $ "--make -shared -dynamic -fPIC -optc-DHAVE_SNPRINTF -lHSrts-ghc" ++Config.cProjectVersion)
|
23
|
+
++ map ("-I"++) extraIncludeDirs
|
24
|
+
|
25
|
+
withTempFile :: String -> String -> IO String
|
26
|
+
withTempFile pattern code = do (name, handle) <- openTempFile "/tmp" pattern
|
27
|
+
hPutStr handle code
|
28
|
+
hClose handle
|
29
|
+
return name
|
30
|
+
|
31
|
+
ghcBuild :: Filename -> String -> String -> [Filename] -> [Filename] -> [String]-> IO (Either String Filename)
|
32
|
+
ghcBuild libFile immediateSource modName extra_sources c_sources args =
|
33
|
+
do -- putStrLn ("modname is " ++ modName)
|
34
|
+
putStrLn immediateSource
|
35
|
+
haskellSrcFile <- withTempFile "hubris_XXXXX.hs" immediateSource
|
36
|
+
putStrLn ("ghc is " ++ ghc)
|
37
|
+
(code, out, err) <- noisySystem ghc $ standardGHCFlags ++ ["-o",libFile,"-optl-Wl,-rpath," ++ libdir,
|
38
|
+
haskellSrcFile, "-L" ++ libdir] ++ extra_sources ++ c_sources ++ args
|
39
|
+
return $ case code of
|
40
|
+
ExitSuccess -> Right libFile
|
41
|
+
otherCode -> Left $ unlines ["Errcode: " ++show code,"output: " ++ out, "error: " ++ err]
|
42
|
+
|
43
|
+
noisySystem :: String -> [String] -> IO (ExitCode, String,String)
|
44
|
+
noisySystem cmd args = (putStrLn . unwords) (cmd:args) >> readProcessWithExitCode cmd args ""
|
45
|
+
|
46
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Language.Ruby.Hubris.Hash where
|
2
|
+
import qualified Language.Ruby.Hubris.Binding as Ruby
|
3
|
+
import Prelude hiding(lookup)
|
4
|
+
import Control.Applicative
|
5
|
+
|
6
|
+
newtype RubyHash = RubyHash Ruby.Value
|
7
|
+
|
8
|
+
-- can only call these functions when we have a ruby interpreter
|
9
|
+
-- initialised. shouldn't usually be a problem, but needs to be
|
10
|
+
-- done when testing from haskell.
|
11
|
+
|
12
|
+
-- to test: does this break horribly when we have multiple threads?
|
13
|
+
|
14
|
+
new = RubyHash <$> Ruby.rb_hash_new
|
15
|
+
insert (RubyHash v) = Ruby.rb_hash_aset v
|
16
|
+
|
17
|
+
-- no Maybe here - we'd just need to test again later, as we're passing
|
18
|
+
-- a Ruby value
|
19
|
+
lookup (RubyHash v) key = Ruby.rb_hash_aref v key
|
20
|
+
|
21
|
+
-- maybe should extract strings?
|
22
|
+
keys :: RubyHash -> IO [Ruby.RValue]
|
23
|
+
keys (RubyHash v) = do Ruby.T_ARRAY res <- Ruby.fromVal <$> Ruby.rb_keys v
|
24
|
+
return res
|
25
|
+
|
26
|
+
toList rhash = keys rhash >>= mapM (\k -> lookup rhash (Ruby.fromRVal k) >>= \v -> return (k,v))
|
27
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
{-# LANGUAGE TemplateHaskell #-}
|
2
|
+
|
3
|
+
module Interpolator where
|
4
|
+
import Language.Haskell.TH
|
5
|
+
import Language.Haskell.TH.Syntax
|
6
|
+
import Language.Haskell.Meta.Parse
|
7
|
+
|
8
|
+
-- The first string in each pair is literal, the second is a variable to
|
9
|
+
-- be interpolated.
|
10
|
+
parse :: String -> [(String, String)]
|
11
|
+
-- parse "Foo#{foo}rah#{foo}" = [("Foo", "foo ++ bar"), ("rah", "foo")]
|
12
|
+
parse str =
|
13
|
+
gen :: [(String, String)] -> Q Exp -> Q Exp
|
14
|
+
gen [] x = x
|
15
|
+
-- gen ((string,variable) : xs) x = gen xs [| $x ++ $(lift string) ++ $(return $ VarE $ mkName variable) |]
|
16
|
+
gen ((string,expr) : xs) x = gen xs [| $x ++ $(lift string) ++ $(return $ lift $ parseExp expr) |]
|
17
|
+
-- gen ((string,variable) : xs) x = gen xs [| $x ++ $(lift string) ++ $(stringE variable) |]
|
18
|
+
|
19
|
+
-- Here we generate the Haskell code for the splice
|
20
|
+
-- from an input format string.
|
21
|
+
interpolate :: String -> Q Exp
|
22
|
+
interpolate s = gen (parse s) [| "" |]
|
@@ -0,0 +1,181 @@
|
|
1
|
+
|
2
|
+
{-# LANGUAGE TemplateHaskell, QuasiQuotes, ScopedTypeVariables #-}
|
3
|
+
module Language.Ruby.Hubris.LibraryBuilder where
|
4
|
+
import Language.Ruby.Hubris
|
5
|
+
import Language.Haskell.Interpreter
|
6
|
+
-- import Language.Haskell.Meta.QQ.HsHere
|
7
|
+
import Language.Ruby.Hubris.GHCBuild
|
8
|
+
|
9
|
+
import List(intersperse)
|
10
|
+
import Data.List(intercalate)
|
11
|
+
import qualified Debug.Trace
|
12
|
+
import Control.Applicative
|
13
|
+
import Control.Monad
|
14
|
+
import Control.Monad.Error.Class
|
15
|
+
import Data.Maybe(catMaybes,fromJust, isJust)
|
16
|
+
|
17
|
+
import GHC(parseStaticFlags, noLoc)
|
18
|
+
import System.IO(hPutStr, hClose, openTempFile)
|
19
|
+
import System.Exit
|
20
|
+
import Language.Ruby.Hubris.ZCode (zenc,Zname(..))
|
21
|
+
|
22
|
+
type Filename = String
|
23
|
+
dotrace a b = b
|
24
|
+
|
25
|
+
-- weirdly, mapMaybeM doesn't exist.
|
26
|
+
mapMaybeM :: (Functor m, Monad m) => (a -> m (Maybe b)) -> [a] -> m [b]
|
27
|
+
mapMaybeM func ls = catMaybes <$> (sequence $ map func ls)
|
28
|
+
|
29
|
+
generateLib :: Filename -> [Filename] -> ModuleName -> [String] -> [String] -> IO (Either Filename String)
|
30
|
+
generateLib libFile sources moduleName buildArgs packages = do
|
31
|
+
-- set up the static args once
|
32
|
+
GHC.parseStaticFlags $ map noLoc $ map ("-package "++) ("hubris":packages)
|
33
|
+
|
34
|
+
s <- generateSource sources moduleName
|
35
|
+
case s of
|
36
|
+
Right (c,mod) -> do bindings <- withTempFile "hubris_interface_XXXXX.c" c
|
37
|
+
ghcBuild libFile mod ("Language.Ruby.Hubris.Exports." ++ moduleName) sources [bindings] buildArgs
|
38
|
+
Left x -> return . Left $ show x
|
39
|
+
|
40
|
+
type Funcname = String
|
41
|
+
type Wrapper = String
|
42
|
+
|
43
|
+
|
44
|
+
callable ::String -> InterpreterT IO (Maybe Int)
|
45
|
+
callable func = do
|
46
|
+
ok <- typeChecks str
|
47
|
+
if not ok
|
48
|
+
then return Nothing
|
49
|
+
else do res <- interpret str (as::Int)
|
50
|
+
return $ Just res
|
51
|
+
where str = "Language.Ruby.Hubris.arity " ++ parens func
|
52
|
+
|
53
|
+
|
54
|
+
-- ok, let's see if we can come up with an expression of the right type
|
55
|
+
exportable :: String -> String -> InterpreterT IO (Maybe (Funcname, Int, Wrapper))
|
56
|
+
exportable moduleName func = do say $ "checking " ++ qualName
|
57
|
+
-- here's the problem - i want callable to return Maybe, not bomb
|
58
|
+
-- all the way back out to the outer runInterpreter
|
59
|
+
match <- callable qualName
|
60
|
+
case match of
|
61
|
+
Nothing -> return Nothing
|
62
|
+
Just i -> do
|
63
|
+
let wrapped = genApp qualName i
|
64
|
+
let eqn = wrapped ++ " == " ++ haskellVal
|
65
|
+
say ("to check: " ++ eqn)
|
66
|
+
checked <- typeChecks eqn
|
67
|
+
say ("Succeeded? " ++ show checked)
|
68
|
+
return $ guard checked>> return (func, i, genWrapper (func,i) moduleName)
|
69
|
+
|
70
|
+
where qualName = moduleName ++ "." ++ func
|
71
|
+
rubyVal = "(fromIntegral $ fromEnum $ Language.Ruby.Hubris.Binding.RUBY_Qtrue)"
|
72
|
+
haskellVal = "(Language.Ruby.Hubris.toHaskell " ++ rubyVal ++ ")"
|
73
|
+
genApp qualName i = unwords (qualName:(take i $ repeat haskellVal))
|
74
|
+
|
75
|
+
generateSource :: [Filename] -> -- optional haskell source to load into the interpreter
|
76
|
+
ModuleName -> -- name of the module to build a wrapper for
|
77
|
+
IO (Either InterpreterError (String,String))
|
78
|
+
generateSource sources moduleName = runInterpreter $ do
|
79
|
+
loadModules sources
|
80
|
+
setImportsQ $ [(mod,Just mod) | mod <- ["Language.Ruby.Hubris","Language.Ruby.Hubris.Binding", moduleName]]
|
81
|
+
funcs <- getFunctions moduleName
|
82
|
+
say ("Candidates: " ++ show funcs)
|
83
|
+
mapM (exportable moduleName) funcs >>= \x -> say (show x)
|
84
|
+
exports :: [(Funcname, Int, Wrapper)] <- mapMaybeM (exportable moduleName) funcs
|
85
|
+
say ("Exportable: " ++ show exports)
|
86
|
+
-- return (undefined, undefined)
|
87
|
+
return (genC [(a,b) | (a,b,_) <- exports] (zenc moduleName),
|
88
|
+
unlines (haskellBoilerplate moduleName:[wrapper | (_,_,wrapper) <- exports]))
|
89
|
+
|
90
|
+
getFunctions moduleName = (\ x -> [a |Fun a <- x]) <$> getModuleExports moduleName
|
91
|
+
|
92
|
+
|
93
|
+
genC :: [(String,Int)] -> Zname -> String
|
94
|
+
genC exports (Zname zmoduleName) = unlines $
|
95
|
+
["#include <stdio.h>"
|
96
|
+
,"#include <stdlib.h>"
|
97
|
+
,"#define HAVE_STRUCT_TIMESPEC 1"
|
98
|
+
,"#include <ruby.h>"
|
99
|
+
-- ,"#define DEBUG 1"
|
100
|
+
,"#ifdef DEBUG"
|
101
|
+
,"#define eprintf printf"
|
102
|
+
,"#else"
|
103
|
+
,"int eprintf(const char *f, ...){}"
|
104
|
+
,"#endif"
|
105
|
+
] ++
|
106
|
+
-- map (("VALUE hubrish_"++) . (++"(VALUE);")) exports ++
|
107
|
+
-- map (("VALUE hubrish_"++) . (++"(VALUE);")) exports ++
|
108
|
+
map cWrapper exports ++
|
109
|
+
["extern void safe_hs_init();"
|
110
|
+
,"extern VALUE Exports;"
|
111
|
+
,"void Init_" ++ zmoduleName ++ "(){"
|
112
|
+
," eprintf(\"loading " ++ zmoduleName ++ "\\n\");"
|
113
|
+
," VALUE Fake = Qnil;"
|
114
|
+
," safe_hs_init();"
|
115
|
+
," Fake = rb_define_module_under(Exports, \"" ++ zmoduleName ++ "\");"
|
116
|
+
] ++ map cDef exports ++ ["}"]
|
117
|
+
where
|
118
|
+
cWrapper :: (String,Int) -> String
|
119
|
+
cWrapper (f,arity) =
|
120
|
+
let res = unlines
|
121
|
+
["VALUE hubrish_" ++ f ++ "("++ (concat . intersperse "," . take arity $ repeat "VALUE") ++ ");",
|
122
|
+
"VALUE " ++ f ++ "(VALUE mod, VALUE v){"
|
123
|
+
," eprintf(\""++f++" has been called\\n\");"
|
124
|
+
-- also needs to curry on the ruby side
|
125
|
+
|
126
|
+
-- v is actually an array now, so we need to stash each element in
|
127
|
+
-- a nested haskell tuple. for the moment, let's just take the first one.
|
128
|
+
|
129
|
+
," unsigned long res = hubrish_" ++ f ++ "(" ++ intercalate "," ["rb_ary_entry(v," ++ show i ++ ")"| i<- [0..(arity-1)]] ++ ");"
|
130
|
+
," eprintf(\"hubrish "++f++" has been called\\n\");"
|
131
|
+
," eprintf(\"result is %p\\n\",res);"
|
132
|
+
-- ," res = res | 0x100000000;"
|
133
|
+
," if (rb_obj_is_kind_of(res,rb_eException)) {"
|
134
|
+
," eprintf(\""++f++" has provoked an exception\\n\");"
|
135
|
+
," rb_exc_raise(res);"
|
136
|
+
," } else {"
|
137
|
+
," eprintf(\"returning from "++f++"\\n\");"
|
138
|
+
," return res;"
|
139
|
+
," }"
|
140
|
+
,"}"]
|
141
|
+
in res
|
142
|
+
|
143
|
+
cDef :: (String,Int) -> String
|
144
|
+
-- adef f = " eprintf(\"Defining |" ++ f ++ "|\\n\");\n" ++ "rb_define_method(Fake, \"" ++ f ++"\","++ f++", 1);"
|
145
|
+
cDef (f,_arity) = " eprintf(\"Defining |" ++ f ++ "|\\n\");\n" ++ "rb_define_method(Fake, \"" ++ f ++"\","++ f++", -2);"
|
146
|
+
|
147
|
+
haskellBoilerplate moduleName = unlines ["{-# LANGUAGE ForeignFunctionInterface, ScopedTypeVariables #-}",
|
148
|
+
"module Language.Ruby.Hubris.Exports." ++ moduleName ++ " where",
|
149
|
+
"import Language.Ruby.Hubris",
|
150
|
+
"import Language.Ruby.Hubris.Binding",
|
151
|
+
"import System.IO.Unsafe (unsafePerformIO)",
|
152
|
+
"import Control.Monad",
|
153
|
+
"import Control.Exception",
|
154
|
+
"import Data.Either",
|
155
|
+
"import Data.Function(($))",
|
156
|
+
"import qualified Prelude as P(show,putStrLn)",
|
157
|
+
"import Data.Tuple (uncurry)",
|
158
|
+
"import Foreign.C.Types",
|
159
|
+
"import qualified " ++ moduleName]
|
160
|
+
|
161
|
+
|
162
|
+
|
163
|
+
-- wrapper = func ++ " b = (Language.Ruby.Hubris.wrap " ++ moduleName ++ "." ++ func ++ ") b",
|
164
|
+
genWrapper (func,arity) mod = unlines $ [func ++ " :: " ++ myType
|
165
|
+
,func ++ " " ++ unwords symbolArgs ++ " = " ++ defHask
|
166
|
+
,"foreign export ccall \"hubrish_" ++ func ++ "\" " ++ func ++ " :: " ++ myType]
|
167
|
+
where myType = intercalate "->" (take (1+arity) $ repeat " CULong ")
|
168
|
+
-- mark's patented gensyms. just awful.
|
169
|
+
symbolArgs = take arity $ map ( \ x -> "fake_arg_symbol_"++[x]) ['a' .. 'z']
|
170
|
+
defHask = "unsafePerformIO $ do\n r <- try $ evaluate $ toRuby $" ++ mod ++"."++ func ++ " " ++ unwords (map (\ x -> "(toHaskell " ++ x ++ ")") symbolArgs) ++ "\n case r of\n" ++
|
171
|
+
-- unlines [" Left (e::SomeException) -> createException (P.show e) `traces` (\"died in haskell wrapper\" P.++ P.show e) ",
|
172
|
+
unlines [" Left (e::SomeException) -> createException (P.show e)" ,
|
173
|
+
" Right a -> return a"]
|
174
|
+
|
175
|
+
say :: String -> InterpreterT IO ()
|
176
|
+
-- say = liftIO . putStrLn
|
177
|
+
say _ = return ()
|
178
|
+
|
179
|
+
-- Local Variables:
|
180
|
+
-- compile-command: "cd ../../../; ./Setup build"
|
181
|
+
-- End:
|