pure 0.1.0 → 0.2.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.
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