pure 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES.rdoc +7 -0
- data/MANIFEST +44 -20
- data/README.rdoc +553 -16
- data/Rakefile +25 -2
- data/devel/jumpstart.rb +606 -253
- data/install.rb +1 -2
- data/lib/pure.rb +38 -16
- data/lib/pure/bundled_parsers.rb +4 -0
- data/lib/pure/bundled_plugin.rb +49 -0
- data/lib/pure/compiler/ruby_parser.rb +63 -0
- data/lib/pure/delegate.rb +16 -0
- data/lib/pure/driver.rb +33 -0
- data/lib/pure/dsl.rb +2 -0
- data/lib/pure/dsl_definition.rb +11 -0
- data/lib/pure/error.rb +89 -0
- data/lib/pure/extracted_functions.rb +11 -0
- data/lib/pure/extractor.rb +59 -0
- data/lib/pure/names.rb +9 -0
- data/lib/pure/native_worker.rb +27 -0
- data/lib/pure/parser/impl/base_parser.rb +21 -0
- data/lib/pure/parser/impl/internal.rb +31 -0
- data/lib/pure/parser/impl/ripper.rb +96 -0
- data/lib/pure/parser/impl/ruby_parser.rb +77 -0
- data/lib/pure/parser/internal.rb +4 -0
- data/lib/pure/parser/ripper.rb +2 -0
- data/lib/pure/parser/ruby_parser.rb +2 -0
- data/lib/pure/pure.rb +32 -0
- data/lib/pure/pure_module.rb +141 -0
- data/lib/pure/util.rb +15 -0
- data/lib/pure/version.rb +4 -0
- data/spec/compiler_ruby_parser_spec.rb +79 -0
- data/spec/compute_overrides_spec.rb +99 -0
- data/spec/compute_spec.rb +86 -0
- data/spec/compute_thread_spec.rb +29 -0
- data/spec/compute_timed_spec.rb +40 -0
- data/spec/delegate_spec.rb +141 -0
- data/spec/fstat_example.rb +26 -0
- data/spec/parser_sexp_spec.rb +100 -0
- data/spec/parser_spec.rb +18 -31
- data/spec/pure_combine_spec.rb +77 -0
- data/spec/pure_def_spec.rb +186 -0
- data/spec/pure_define_method_spec.rb +24 -0
- data/spec/pure_eval_spec.rb +18 -0
- data/spec/pure_fun_spec.rb +243 -0
- data/spec/pure_nested_spec.rb +35 -0
- data/spec/pure_parser_spec.rb +50 -0
- data/spec/pure_spec.rb +81 -0
- data/spec/pure_spec_base.rb +106 -0
- data/spec/pure_splat_spec.rb +18 -0
- data/spec/pure_two_defs_spec.rb +20 -0
- data/spec/pure_worker_spec.rb +33 -0
- data/spec/readme_spec.rb +36 -32
- data/spec/splat_spec.rb +12 -11
- data/spec/worker_spec.rb +89 -0
- metadata +157 -41
- data/devel/jumpstart/lazy_attribute.rb +0 -38
- data/devel/jumpstart/ruby.rb +0 -44
- data/devel/jumpstart/simple_installer.rb +0 -85
- data/lib/pure/pure_private/creator.rb +0 -27
- data/lib/pure/pure_private/driver.rb +0 -48
- data/lib/pure/pure_private/error.rb +0 -32
- data/lib/pure/pure_private/extractor.rb +0 -79
- data/lib/pure/pure_private/extractor_ripper.rb +0 -95
- data/lib/pure/pure_private/extractor_ruby_parser.rb +0 -47
- data/lib/pure/pure_private/function_database.rb +0 -10
- data/lib/pure/pure_private/singleton_features.rb +0 -67
- data/lib/pure/pure_private/util.rb +0 -23
- data/spec/basic_spec.rb +0 -38
- data/spec/combine_spec.rb +0 -62
- data/spec/common.rb +0 -44
- data/spec/error_spec.rb +0 -146
- data/spec/fun_spec.rb +0 -122
- data/spec/lazy_spec.rb +0 -22
- data/spec/subseqent_spec.rb +0 -42
- data/spec/timed_spec.rb +0 -30
@@ -0,0 +1,99 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
describe "compute" do
|
4
|
+
describe "overrides" do
|
5
|
+
it "should replace missing functions" do
|
6
|
+
pure do
|
7
|
+
def add(x, y)
|
8
|
+
x + y
|
9
|
+
end
|
10
|
+
end.compute(4, :x => 33, :y => 44).add.should == 77
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should fail if underspecified" do
|
14
|
+
(1..4).each { |n|
|
15
|
+
lambda {
|
16
|
+
pure do
|
17
|
+
def add(x, y)
|
18
|
+
x + y
|
19
|
+
end
|
20
|
+
end.compute(n, :y => 44).add
|
21
|
+
}.should raise_error(Pure::NoFunctionError, "no function named `x'")
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should override no-argument functions" do
|
26
|
+
(1..4).each { |n|
|
27
|
+
pure do
|
28
|
+
def add(x, y)
|
29
|
+
x + y
|
30
|
+
end
|
31
|
+
def x
|
32
|
+
33
|
33
|
+
end
|
34
|
+
def y
|
35
|
+
44
|
36
|
+
end
|
37
|
+
end.compute(n, :x => 55).add.should == 99
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should override multi-argument functions" do
|
42
|
+
(1..4).each { |n|
|
43
|
+
pure do
|
44
|
+
def add(x, y)
|
45
|
+
x + y
|
46
|
+
end
|
47
|
+
def x
|
48
|
+
33
|
49
|
+
end
|
50
|
+
def y
|
51
|
+
44
|
52
|
+
end
|
53
|
+
end.compute(n, :add => 11).add.should == 11
|
54
|
+
}
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should override a root function" do
|
58
|
+
(1..4).each { |n|
|
59
|
+
greet = pure do
|
60
|
+
def hello(name)
|
61
|
+
"Hello, #{name}."
|
62
|
+
end
|
63
|
+
|
64
|
+
def name
|
65
|
+
"Bob"
|
66
|
+
end
|
67
|
+
end
|
68
|
+
greet.compute(n).hello.should eql("Hello, Bob.")
|
69
|
+
greet.compute(n, :name => "Ralph").hello.should eql("Hello, Ralph.")
|
70
|
+
greet.compute(n, :hello => "Goodbye.").hello.should eql("Goodbye.")
|
71
|
+
}
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should prevent the overridden function from being called" do
|
75
|
+
count = 0
|
76
|
+
pure do
|
77
|
+
def f x
|
78
|
+
x + 44
|
79
|
+
end
|
80
|
+
|
81
|
+
fun :x do
|
82
|
+
count += 1
|
83
|
+
33
|
84
|
+
end
|
85
|
+
end.compute(:x => 11).f.should eql(55)
|
86
|
+
count.should == 0
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should convert non-symbol keys to symbols" do
|
90
|
+
(1..4).each { |n|
|
91
|
+
pure do
|
92
|
+
def add(x, y)
|
93
|
+
x + y
|
94
|
+
end
|
95
|
+
end.compute(n, "x" => 55, "y" => 44).add.should == 99
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
describe "compute" do
|
4
|
+
it "should accept no arguments" do
|
5
|
+
pure do
|
6
|
+
def f
|
7
|
+
33
|
8
|
+
end
|
9
|
+
end.compute.f.should == 33
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should accept (overrides) argument" do
|
13
|
+
pure do
|
14
|
+
def f
|
15
|
+
33
|
16
|
+
end
|
17
|
+
end.compute(:f => 44).f.should == 44
|
18
|
+
end
|
19
|
+
it "should accept (num_parallel) argument" do
|
20
|
+
(1..8).each { |n|
|
21
|
+
pure do
|
22
|
+
def f
|
23
|
+
33
|
24
|
+
end
|
25
|
+
end.compute(n).f.should == 33
|
26
|
+
}
|
27
|
+
end
|
28
|
+
it "should accept (worker) argument" do
|
29
|
+
pure do
|
30
|
+
def f
|
31
|
+
33
|
32
|
+
end
|
33
|
+
end.compute(Pure::NativeWorker).f.should == 33
|
34
|
+
end
|
35
|
+
it "should accept (num_parallel, overrides) arguments" do
|
36
|
+
(1..8).each { |n|
|
37
|
+
pure do
|
38
|
+
def f
|
39
|
+
33
|
40
|
+
end
|
41
|
+
end.compute(n, :f => 99).f.should == 99
|
42
|
+
}
|
43
|
+
end
|
44
|
+
it "should accept (worker, overrides) arguments" do
|
45
|
+
pure do
|
46
|
+
def f
|
47
|
+
33
|
48
|
+
end
|
49
|
+
end.compute(Pure::NativeWorker, :f => 99).f.should == 99
|
50
|
+
end
|
51
|
+
it "should raise error for > 2 arguments" do
|
52
|
+
lambda {
|
53
|
+
pure do
|
54
|
+
def f
|
55
|
+
33
|
56
|
+
end
|
57
|
+
end.compute(:a, :b, :c).f.should == 99
|
58
|
+
}.should raise_error(ArgumentError)
|
59
|
+
end
|
60
|
+
it "should raise #{Pure::NoFunctionError} for undefined function" do
|
61
|
+
(1..8).each { |n|
|
62
|
+
lambda {
|
63
|
+
result = pure do
|
64
|
+
def f(x)
|
65
|
+
x + 33
|
66
|
+
end
|
67
|
+
end.compute(n).f
|
68
|
+
}.should raise_error(Pure::NoFunctionError, "no function named `x'")
|
69
|
+
}
|
70
|
+
end
|
71
|
+
it "should be independent of other compute calls" do
|
72
|
+
mod_f1 = pure do
|
73
|
+
def f
|
74
|
+
1
|
75
|
+
end
|
76
|
+
end
|
77
|
+
mod_f2 = pure do
|
78
|
+
def f
|
79
|
+
2
|
80
|
+
end
|
81
|
+
end
|
82
|
+
result_f1 = mod_f1.compute(3)
|
83
|
+
result_f2 = mod_f2.compute(4)
|
84
|
+
result_f1.f.should == 1
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
describe "compute" do
|
4
|
+
max_threads = 50
|
5
|
+
describe "with number of threads 1..#{max_threads}" do
|
6
|
+
it "should succeed" do
|
7
|
+
mod = pure do
|
8
|
+
def area(width, height)
|
9
|
+
width*height
|
10
|
+
end
|
11
|
+
|
12
|
+
def width(border)
|
13
|
+
20 + border
|
14
|
+
end
|
15
|
+
|
16
|
+
def height(border)
|
17
|
+
30 + border
|
18
|
+
end
|
19
|
+
|
20
|
+
def border
|
21
|
+
5
|
22
|
+
end
|
23
|
+
end
|
24
|
+
(1..max_threads).each { |n|
|
25
|
+
mod.compute(n) { |s| s.area }.should == (20 + 5)*(30 + 5)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
require 'benchmark'
|
3
|
+
|
4
|
+
# factor out due to parsers changing
|
5
|
+
def create_timed_benchmark
|
6
|
+
mod = pure do
|
7
|
+
def root(a, b)
|
8
|
+
end
|
9
|
+
|
10
|
+
def a
|
11
|
+
sleep(0.25)
|
12
|
+
end
|
13
|
+
|
14
|
+
def b
|
15
|
+
sleep(0.25)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
lambda { |n|
|
19
|
+
mod.compute(n)[:root]
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
require 'rbconfig'
|
24
|
+
|
25
|
+
# uneven results in windows
|
26
|
+
unless Config::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)!
|
27
|
+
describe "compute" do
|
28
|
+
describe "with timed example" do
|
29
|
+
it "should run with 1 thread" do
|
30
|
+
compute = create_timed_benchmark
|
31
|
+
Benchmark.measure { compute.call(1) }.real.should be_close(0.5, 0.1)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should be 2x faster with 2 threads" do
|
35
|
+
compute = create_timed_benchmark
|
36
|
+
Benchmark.measure { compute.call(2) }.real.should be_close(0.25, 0.05)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
if RUBY_PLATFORM == "java"
|
4
|
+
# jruby needs this here; don't know why
|
5
|
+
require 'ruby2ruby'
|
6
|
+
end
|
7
|
+
|
8
|
+
def create_geometry_example
|
9
|
+
pure do
|
10
|
+
def area(width, height)
|
11
|
+
width*height
|
12
|
+
end
|
13
|
+
|
14
|
+
def width(border)
|
15
|
+
7 + border
|
16
|
+
end
|
17
|
+
|
18
|
+
def height(border)
|
19
|
+
5 + border
|
20
|
+
end
|
21
|
+
end.compute(4, :border => 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
describe Pure::Delegate do
|
25
|
+
it "should hold results" do
|
26
|
+
geometry = create_geometry_example
|
27
|
+
geometry.area.should eql((7 + 2)*(5 + 2))
|
28
|
+
geometry.width.should eql(7 + 2)
|
29
|
+
geometry.height.should eql(5 + 2)
|
30
|
+
geometry.border.should eql(2)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should raise error for undefined attribute" do
|
34
|
+
lambda {
|
35
|
+
pure do
|
36
|
+
def f
|
37
|
+
33
|
38
|
+
end
|
39
|
+
end.compute.g
|
40
|
+
}.should raise_error(NoMethodError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should raise error when attribute is passed args" do
|
44
|
+
lambda {
|
45
|
+
pure do
|
46
|
+
def f
|
47
|
+
33
|
48
|
+
end
|
49
|
+
end.compute.f(44)
|
50
|
+
}.should raise_error(ArgumentError)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be lazy" do
|
54
|
+
mutex = Mutex.new
|
55
|
+
counter = 0
|
56
|
+
incrementer = pure do
|
57
|
+
fun :increment do
|
58
|
+
mutex.synchronize {
|
59
|
+
counter += 1
|
60
|
+
}
|
61
|
+
end
|
62
|
+
end.compute(Pure::NativeWorker)
|
63
|
+
counter.should eql(0)
|
64
|
+
3.times {
|
65
|
+
incrementer.increment.should eql(1)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
describe "[] alternative to method call" do
|
70
|
+
it "should accept symbols" do
|
71
|
+
geometry = create_geometry_example
|
72
|
+
geometry[:area].should eql((7 + 2)*(5 + 2))
|
73
|
+
geometry[:width].should eql(7 + 2)
|
74
|
+
geometry[:height].should eql(5 + 2)
|
75
|
+
geometry[:border].should eql(2)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should accept strings" do
|
79
|
+
geometry = create_geometry_example
|
80
|
+
geometry["area"].should eql((7 + 2)*(5 + 2))
|
81
|
+
geometry["width"].should eql(7 + 2)
|
82
|
+
geometry["height"].should eql(5 + 2)
|
83
|
+
geometry["border"].should eql(2)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should raise error if given > 1 arg" do
|
87
|
+
geometry = create_geometry_example
|
88
|
+
lambda {
|
89
|
+
geometry[:area, 4]
|
90
|
+
}.should raise_error(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise error when given other types" do
|
94
|
+
lambda {
|
95
|
+
pure do
|
96
|
+
def f
|
97
|
+
end
|
98
|
+
end.compute(33)[nil]
|
99
|
+
}.should raise_error(TypeError)
|
100
|
+
|
101
|
+
lambda {
|
102
|
+
pure do
|
103
|
+
def f
|
104
|
+
end
|
105
|
+
end.compute(33)[Object.new]
|
106
|
+
}.should raise_error(TypeError)
|
107
|
+
end
|
108
|
+
|
109
|
+
it "should propagate exceptions from root function" do
|
110
|
+
mod = pure do
|
111
|
+
def f
|
112
|
+
raise "zz"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
result = mod.compute(4)
|
116
|
+
lambda {
|
117
|
+
result.f
|
118
|
+
}.should raise_error(RuntimeError, "zz")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should propagate exceptions from child functions" do
|
122
|
+
mod = pure do
|
123
|
+
def f(x)
|
124
|
+
33
|
125
|
+
end
|
126
|
+
|
127
|
+
def x(y)
|
128
|
+
44
|
129
|
+
end
|
130
|
+
|
131
|
+
def y
|
132
|
+
raise "foo"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
result = mod.compute(4)
|
136
|
+
lambda {
|
137
|
+
result.f
|
138
|
+
}.should raise_error(RuntimeError, "foo")
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
describe "fstat example" do
|
4
|
+
it "should succeed" do
|
5
|
+
files = Dir["*"]
|
6
|
+
|
7
|
+
stats = pure do
|
8
|
+
files.each { |file|
|
9
|
+
fun file do
|
10
|
+
File.stat(fun_name.to_s)
|
11
|
+
end
|
12
|
+
}
|
13
|
+
|
14
|
+
fun :total_size => files do |*values|
|
15
|
+
values.inject(0) { |acc, stat| acc + stat.size }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
stats.compute { |result|
|
20
|
+
result["Rakefile"].size.should eql(File.stat("Rakefile").size)
|
21
|
+
total = files.inject(0) { |acc, file| acc + File.stat(file).size }
|
22
|
+
result.total_size.should == total
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/pure_spec_base'
|
2
|
+
|
3
|
+
def check_sexp(type, mod, name, expected)
|
4
|
+
entry = Pure::ExtractedFunctions[Pure.parser][mod][name]
|
5
|
+
if Pure.parser == (Pure::Parser::RubyParser rescue nil)
|
6
|
+
check_ruby_parser_sexp(entry, type, mod, expected)
|
7
|
+
elsif Pure.parser == (Pure::Parser::Ripper rescue nil)
|
8
|
+
check_ripper_sexp(entry, type, mod, expected)
|
9
|
+
elsif Pure.parser == (Pure::Parser::Internal rescue nil)
|
10
|
+
check_internal_sexp(entry, type, mod, expected)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def check_ruby_parser_sexp(entry, type, mod, expected)
|
15
|
+
entry[:code].should be_a(Sexp)
|
16
|
+
sexp = Pure::Parser::RubyParser::DupSexp.dup_sexp(entry[:code])
|
17
|
+
require 'ruby2ruby'
|
18
|
+
recovered = Ruby2Ruby.new.process(sexp).strip.gsub(%r!\s+!, " ")
|
19
|
+
recovered.should == expected
|
20
|
+
end
|
21
|
+
|
22
|
+
def check_ripper_sexp(entry, type, mod, expected)
|
23
|
+
sexp = entry[:code]
|
24
|
+
sexp.should be_a(Array)
|
25
|
+
case entry[:origin]
|
26
|
+
when :def
|
27
|
+
sexp.first.should == :def
|
28
|
+
when :fun
|
29
|
+
sexp.first.should == :do_block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def check_internal_sexp(entry, type, mod, expected)
|
34
|
+
entry[:code].should == nil
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "parser" do
|
38
|
+
describe "sexp" do
|
39
|
+
it "should match the source" do
|
40
|
+
def_f = pure do
|
41
|
+
def f(x, y)
|
42
|
+
x + y
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
fun_f = pure do
|
47
|
+
fun :f => [:x, :y] do |a, b|
|
48
|
+
a + b
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def_g = pure do
|
53
|
+
def g
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
fun_g = pure do
|
58
|
+
fun :g do
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def_h = pure do
|
63
|
+
def h(x)
|
64
|
+
x**2
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
fun_h = pure do
|
69
|
+
fun :h => :x do |a|
|
70
|
+
a**2
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
fun_i = pure do
|
75
|
+
fun :i => [:p, :q] do |*s|
|
76
|
+
s.size
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
fun_j = pure do
|
81
|
+
fun :j => [:p, :q] do |r, *s|
|
82
|
+
r + s
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
[
|
87
|
+
[:def, def_f, :f, "def f(x, y) (x + y) end"],
|
88
|
+
[:fun, fun_f, :f, "fun(:f => ([:x, :y])) { |a, b| (a + b) }"],
|
89
|
+
[:def, def_g, :g, "def g # do nothing end"],
|
90
|
+
[:fun, fun_g, :g, "fun(:g) { }"],
|
91
|
+
[:def, def_h, :h, "def h(x) (x ** 2) end"],
|
92
|
+
[:fun, fun_h, :h, "fun(:h => :x) { |a| (a ** 2) }"],
|
93
|
+
[:fun, fun_i, :i, "fun(:i => ([:p, :q])) { |*s| s.size }"],
|
94
|
+
[:fun, fun_j, :j, "fun(:j => ([:p, :q])) { |r, *s| (r + s) }"],
|
95
|
+
].each { |type, mod, name, expected|
|
96
|
+
check_sexp(type, mod, name, expected)
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|