pure 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (75) hide show
  1. data/CHANGES.rdoc +7 -0
  2. data/MANIFEST +44 -20
  3. data/README.rdoc +553 -16
  4. data/Rakefile +25 -2
  5. data/devel/jumpstart.rb +606 -253
  6. data/install.rb +1 -2
  7. data/lib/pure.rb +38 -16
  8. data/lib/pure/bundled_parsers.rb +4 -0
  9. data/lib/pure/bundled_plugin.rb +49 -0
  10. data/lib/pure/compiler/ruby_parser.rb +63 -0
  11. data/lib/pure/delegate.rb +16 -0
  12. data/lib/pure/driver.rb +33 -0
  13. data/lib/pure/dsl.rb +2 -0
  14. data/lib/pure/dsl_definition.rb +11 -0
  15. data/lib/pure/error.rb +89 -0
  16. data/lib/pure/extracted_functions.rb +11 -0
  17. data/lib/pure/extractor.rb +59 -0
  18. data/lib/pure/names.rb +9 -0
  19. data/lib/pure/native_worker.rb +27 -0
  20. data/lib/pure/parser/impl/base_parser.rb +21 -0
  21. data/lib/pure/parser/impl/internal.rb +31 -0
  22. data/lib/pure/parser/impl/ripper.rb +96 -0
  23. data/lib/pure/parser/impl/ruby_parser.rb +77 -0
  24. data/lib/pure/parser/internal.rb +4 -0
  25. data/lib/pure/parser/ripper.rb +2 -0
  26. data/lib/pure/parser/ruby_parser.rb +2 -0
  27. data/lib/pure/pure.rb +32 -0
  28. data/lib/pure/pure_module.rb +141 -0
  29. data/lib/pure/util.rb +15 -0
  30. data/lib/pure/version.rb +4 -0
  31. data/spec/compiler_ruby_parser_spec.rb +79 -0
  32. data/spec/compute_overrides_spec.rb +99 -0
  33. data/spec/compute_spec.rb +86 -0
  34. data/spec/compute_thread_spec.rb +29 -0
  35. data/spec/compute_timed_spec.rb +40 -0
  36. data/spec/delegate_spec.rb +141 -0
  37. data/spec/fstat_example.rb +26 -0
  38. data/spec/parser_sexp_spec.rb +100 -0
  39. data/spec/parser_spec.rb +18 -31
  40. data/spec/pure_combine_spec.rb +77 -0
  41. data/spec/pure_def_spec.rb +186 -0
  42. data/spec/pure_define_method_spec.rb +24 -0
  43. data/spec/pure_eval_spec.rb +18 -0
  44. data/spec/pure_fun_spec.rb +243 -0
  45. data/spec/pure_nested_spec.rb +35 -0
  46. data/spec/pure_parser_spec.rb +50 -0
  47. data/spec/pure_spec.rb +81 -0
  48. data/spec/pure_spec_base.rb +106 -0
  49. data/spec/pure_splat_spec.rb +18 -0
  50. data/spec/pure_two_defs_spec.rb +20 -0
  51. data/spec/pure_worker_spec.rb +33 -0
  52. data/spec/readme_spec.rb +36 -32
  53. data/spec/splat_spec.rb +12 -11
  54. data/spec/worker_spec.rb +89 -0
  55. metadata +157 -41
  56. data/devel/jumpstart/lazy_attribute.rb +0 -38
  57. data/devel/jumpstart/ruby.rb +0 -44
  58. data/devel/jumpstart/simple_installer.rb +0 -85
  59. data/lib/pure/pure_private/creator.rb +0 -27
  60. data/lib/pure/pure_private/driver.rb +0 -48
  61. data/lib/pure/pure_private/error.rb +0 -32
  62. data/lib/pure/pure_private/extractor.rb +0 -79
  63. data/lib/pure/pure_private/extractor_ripper.rb +0 -95
  64. data/lib/pure/pure_private/extractor_ruby_parser.rb +0 -47
  65. data/lib/pure/pure_private/function_database.rb +0 -10
  66. data/lib/pure/pure_private/singleton_features.rb +0 -67
  67. data/lib/pure/pure_private/util.rb +0 -23
  68. data/spec/basic_spec.rb +0 -38
  69. data/spec/combine_spec.rb +0 -62
  70. data/spec/common.rb +0 -44
  71. data/spec/error_spec.rb +0 -146
  72. data/spec/fun_spec.rb +0 -122
  73. data/spec/lazy_spec.rb +0 -22
  74. data/spec/subseqent_spec.rb +0 -42
  75. 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