rubiskell 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README ADDED
@@ -0,0 +1,19 @@
1
+ ! Rubiskell
2
+
3
+ !! What's this?
4
+ Rubiskell allows you to call haskell function from ruby script.
5
+
6
+ !! Example
7
+ require 'rubiskell'
8
+
9
+ FIB = <<EOD
10
+ fib :: Int -> Int
11
+ fib 0 = 0
12
+ fib 1 = 1
13
+ fib n = fib (n-2) + fib (n-1)
14
+ EOD
15
+
16
+ fib = Haskell.new(FIB)
17
+ puts "fib 5 is #{fib.run(5)}."
18
+
19
+ you can also write fib[5] instead of fib.run(5).
@@ -0,0 +1,4 @@
1
+ fib :: Int -> Int
2
+ fib 0 = 0
3
+ fib 1 = 1
4
+ fib n = fib (n-2) + fib (n-1)
@@ -0,0 +1,7 @@
1
+ require 'rubiskell'
2
+
3
+ def fib(n)
4
+ Haskell.load("fib.hs").run(n)
5
+ end
6
+
7
+ puts "fib 5 is #{fib(5)}."
@@ -0,0 +1,2 @@
1
+ plus :: Int -> Int -> Int
2
+ plus x y = x + y
@@ -0,0 +1,7 @@
1
+ require 'rubiskell'
2
+
3
+ def plus(x, y)
4
+ Haskell.load("plus.hs").run(x, y)
5
+ end
6
+
7
+ puts "5 + 6 is #{plus(5, 6)}."
@@ -0,0 +1,12 @@
1
+ require 'rubiskell'
2
+
3
+ FIB = <<EOD
4
+ fib :: Int -> Int
5
+ fib 0 = 0
6
+ fib 1 = 1
7
+ fib n = fib (n-2) + fib (n-1)
8
+ EOD
9
+
10
+ fib = Haskell.new(FIB)
11
+
12
+ puts "fib 5 is #{fib[5]}."
@@ -0,0 +1,114 @@
1
+ require 'tmpdir'
2
+
3
+ class Haskell
4
+ def self.load(fname)
5
+ new(File.read(fname))
6
+ end
7
+
8
+ def self.tuple_form(args)
9
+ a = args.join(",")
10
+ return args.size == 1 ? a : "(#{a})"
11
+ end
12
+
13
+ def initialize(code, option={})
14
+ @interpreter = option[:interpreter] || "runghc"
15
+ @signetures = parse_signetures(code)
16
+ @code = code
17
+ @k = 0
18
+ end
19
+
20
+ def call(sig, *args)
21
+ if String === sig
22
+ sig = @signetures.find{|sig| sig.name == sig}
23
+ raise ArgumentError, "unknown method" if sig.nil?
24
+ end
25
+
26
+ src = make_src(@code, sig)
27
+ path = make_tempfile(src)
28
+ cmd = make_cmd(path, args)
29
+ convert_result(`#{cmd}`.chomp, sig.ret)
30
+ end
31
+
32
+ def run(*args)
33
+ call(@signetures[0], *args)
34
+ end
35
+ alias [] run
36
+
37
+ def make_cmd(path, args)
38
+ arg_list = Haskell.tuple_form(args.map{|x| x.inspect})
39
+ arg_list.gsub!(/"/, '\\"')
40
+ %Q<#{@interpreter} "#{path}" "#{arg_list}">
41
+ end
42
+
43
+ def convert_result(result, type)
44
+ case type.to_s
45
+ when "Integer"
46
+ result.to_i
47
+ when "String"
48
+ result[1..-2] #cut \"
49
+ else
50
+ raise "unknown type: #{type}"
51
+ end
52
+ end
53
+
54
+ TYPE_TABLE = [
55
+ ["Int", Integer],
56
+ ["String", String],
57
+ ]
58
+ class Signeture
59
+ def initialize(name, args, ret)
60
+ @name, @args, @ret = name, args, ret
61
+ end
62
+ attr_reader :name, :args, :ret
63
+
64
+ def tuple_form
65
+ Haskell.tuple_form(@args.map{|ty|TYPE_TABLE.rassoc(ty)[0]})
66
+ end
67
+
68
+ def tuple_vars
69
+ "(" + make_vars().join(",") + ")"
70
+ end
71
+
72
+ def list_vars
73
+ make_vars().join(" ")
74
+ end
75
+
76
+ # arity must be <= 26 :P
77
+ def make_vars
78
+ ("a".."z").to_a.slice(0, @args.size)
79
+ end
80
+ end
81
+
82
+ def parse_signetures(code)
83
+ code.scan(/^(\w+)\s*::\s*(\w+(\s*->\s*\w+)*)\s*$/).map do |sig|
84
+ name = $1.strip
85
+ args = $2.split(/->/).map{|x|
86
+ pair = TYPE_TABLE.assoc(x.strip)
87
+ pair ? pair[1] : (raise ArgumentError, "unknown Haskell type #{x.strip}")
88
+ }
89
+ ret = args.pop
90
+ Signeture.new(name, args, ret)
91
+ end
92
+ end
93
+
94
+ def make_src(code, sig)
95
+ src = "import System\n"
96
+ src << code
97
+ src << "\n"
98
+ src << "main = do args <- getArgs\n"
99
+ src << " print $ apply #{sig.name} $ _read $ head args\n"
100
+ src << " where\n"
101
+ src << " _read :: String -> #{sig.tuple_form}\n"
102
+ src << " _read = read\n"
103
+ src << " apply f #{sig.tuple_vars} = f #{sig.list_vars}\n"
104
+ end
105
+
106
+ def make_tempfile(src)
107
+ while File.exist?(fname = "rubiskell.#{Process.pid}.#{@k}.hs")
108
+ @k += 1
109
+ end
110
+ open(fname, "w"){|f| f.write src }
111
+ at_exit{ File.unlink(fname) }
112
+ fname
113
+ end
114
+ end
@@ -0,0 +1,4 @@
1
+ fib :: Int -> Int
2
+ fib 0 = 0
3
+ fib 1 = 1
4
+ fib n = fib (n-2) + fib (n-1)
@@ -0,0 +1,61 @@
1
+ #
2
+ # spec_rubiskell.rb - Unit test with RSpec
3
+ #
4
+ $LOAD_PATH << File.expand_path("../lib", File.dirname(__FILE__))
5
+ require 'rubiskell'
6
+
7
+ FIB = <<EOD
8
+ fib :: Int -> Int
9
+ fib 0 = 0
10
+ fib 1 = 1
11
+ fib n = fib (n-2) + fib (n-1)
12
+ EOD
13
+
14
+ MANY_ARGS = <<EOD
15
+ foo :: String -> Int -> String -> String
16
+ foo s1 n s2 = s1 ++ (show n) ++ s2
17
+ EOD
18
+
19
+ describe "parse_signetures" do
20
+ before(:each) do
21
+ @hs = Haskell.new("")
22
+ end
23
+
24
+ it "should parse Int -> Int" do
25
+ sigs = @hs.parse_signetures(FIB)
26
+ sigs.should have(1).items
27
+
28
+ sig = sigs.first
29
+ sig.name.should == "fib"
30
+ sig.args.should == [Integer]
31
+ sig.ret.should == Integer
32
+ sig.tuple_form.should == "Int"
33
+ end
34
+
35
+ it "should parse many args" do
36
+ sigs = @hs.parse_signetures("foo :: String->Int->String -> Int")
37
+ sig = sigs.first
38
+ sig.name.should == "foo"
39
+ sig.args.should == [String, Integer, String]
40
+ sig.ret.should == Integer
41
+ sig.tuple_form.should == "(String,Int,String)"
42
+ end
43
+ end
44
+
45
+ describe "Rubiskell" do
46
+ it "should run fib" do
47
+ fib = Haskell.new(FIB)
48
+ fib[2].should == 1
49
+ fib[3].should == 2
50
+ end
51
+
52
+ it "should pass many args" do
53
+ many = Haskell.new(MANY_ARGS)
54
+ many["1", 2, "3"].should == "123"
55
+ end
56
+
57
+ it "should run file" do
58
+ fib = Haskell.load(File.expand_path("fib.hs", File.dirname(__FILE__)))
59
+ fib[2].should == 1
60
+ end
61
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubiskell
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - yhara (Yutaka HARA)
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-07-07 00:00:00 +09:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description:
17
+ email: yhara.at.kmc.gr.jp
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - README
26
+ - lib/rubiskell.rb
27
+ - example/fib.hs
28
+ - example/fib.rb
29
+ - example/plus.hs
30
+ - example/plus.rb
31
+ - example/sample.rb
32
+ - spec/fib.hs
33
+ - spec/spec_rubiskell.rb
34
+ has_rdoc: false
35
+ homepage:
36
+ post_install_message:
37
+ rdoc_options: []
38
+
39
+ require_paths:
40
+ - lib
41
+ required_ruby_version: !ruby/object:Gem::Requirement
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ version: "0"
46
+ version:
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ requirements: []
54
+
55
+ rubyforge_project: rubiskell
56
+ rubygems_version: 1.2.0
57
+ signing_key:
58
+ specification_version: 2
59
+ summary: Run haskell snippet from ruby
60
+ test_files: []
61
+