rubiskell 0.1.0

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/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
+