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
@@ -1,36 +1,23 @@
1
- require File.dirname(__FILE__) + "/common"
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
2
 
3
- describe "parse engine" do
4
- it "should be queryable" do
5
- pure do
6
- def f
3
+ describe "parser" do
4
+ describe "provided by user" do
5
+ it "should be accepted by pure" do
6
+ memo = nil
7
+ parser = Class.new do
8
+ define_method :extract do |*args|
9
+ memo = args
10
+ Hash.new
11
+ end
12
+ def name
13
+ "SampleParser"
14
+ end
15
+ end.new
16
+ mod = pure(parser) do
17
+ def f(x, y)
18
+ end
7
19
  end
8
- end
9
- lambda {
10
- Pure.parser
11
- }.should_not raise_error
12
- end
13
-
14
- it "should be swappable" do
15
- previous = Pure.parser
16
- begin
17
- Pure.parser = "ruby_parser"
18
- Pure.parser.should == "ruby_parser"
19
- ensure
20
- Pure.parser = previous
21
- end
22
- end
23
-
24
- it "should have a default unless Method#parameters available" do
25
- Pure.parser = nil
26
- pure do
27
- def f
28
- end
29
- end.compute(:f, 3)
30
- if Method.instance_methods.include?(:parameters)
31
- Pure.parser.should == nil
32
- else
33
- Pure.parser.should_not == nil
20
+ memo.should == [mod, :f, __FILE__, 17]
34
21
  end
35
22
  end
36
23
  end
@@ -0,0 +1,77 @@
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
+
3
+ # factor out due to parsers changing
4
+ module PureCombineSpec
5
+ module_function
6
+
7
+ def create_mod_a
8
+ pure do
9
+ def area(width, height)
10
+ width*height
11
+ end
12
+
13
+ def border
14
+ 5
15
+ end
16
+ end
17
+ end
18
+
19
+ def create_mod_b
20
+ pure do
21
+ def width(border)
22
+ 20 + border
23
+ end
24
+
25
+ def height(border)
26
+ 30 + border
27
+ end
28
+ end
29
+ end
30
+
31
+ def create_combined
32
+ mod_a = create_mod_a
33
+ mod_b = create_mod_b
34
+ pure do
35
+ include mod_a
36
+ include mod_b
37
+ end
38
+ end
39
+ end
40
+
41
+ describe "pure" do
42
+ describe "modules combined with other pure modules" do
43
+ max_threads = 5
44
+
45
+ it "should work with modules included into empty module" do
46
+ combined = PureCombineSpec.create_combined
47
+ (1..max_threads).each { |n|
48
+ combined.compute(n).area.should == (20 + 5)*(30 + 5)
49
+ }
50
+ end
51
+
52
+ it "should work with modules included into overriding module" do
53
+ combined = PureCombineSpec.create_combined
54
+ combined_override = pure do
55
+ include combined
56
+ def border
57
+ 99
58
+ end
59
+ end
60
+ (1..max_threads).each { |n|
61
+ combined_override.compute(n).area.should == (20 + 99)*(30 + 99)
62
+ }
63
+ end
64
+
65
+ it "should work with one module included into another" do
66
+ mod_a = PureCombineSpec.create_mod_a
67
+ mod_b = PureCombineSpec.create_mod_b
68
+ mod_a.module_eval do
69
+ include mod_b
70
+ end
71
+ (1..max_threads).each { |n|
72
+ mod_a.compute(n).area.should == (20 + 5)*(30 + 5)
73
+ }
74
+ end
75
+ end
76
+ end
77
+
@@ -0,0 +1,186 @@
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
+
3
+ describe "pure" do
4
+ describe "`def' definitions" do
5
+ it "should be parsed with 1 arg default" do
6
+ lambda {
7
+ pure do
8
+ def f(x = 99)
9
+ x
10
+ end
11
+ end
12
+ }.should raise_error(
13
+ Pure::DefaultArgumentError,
14
+ "cannot use default argument in pure function at #{__FILE__}:8"
15
+ )
16
+ end
17
+
18
+ it "should be parsed with non-paren 1 arg" do
19
+ pure do
20
+ def f x
21
+ x + 33
22
+ end
23
+ end.compute(3, :x => 44).f.should == 77
24
+ end
25
+
26
+ it "should be parsed with non-paren 1 arg splat" do
27
+ lambda {
28
+ pure do
29
+ def f *x
30
+ x + 33
31
+ end
32
+ end
33
+ }.should raise_error(Pure::SplatError)
34
+ end
35
+
36
+ it "should be parsed with non-paren 2 arg splat" do
37
+ lambda {
38
+ pure do
39
+ def f x, *y
40
+ x + 33
41
+ end
42
+ end
43
+ }.should raise_error(Pure::SplatError)
44
+ end
45
+
46
+ it "should be parsed with non-paren 2 args" do
47
+ pure do
48
+ def f x, y
49
+ x + y
50
+ end
51
+ end.compute(3, :x => 33, :y => 44).f.should == 77
52
+ end
53
+
54
+ it "should be parsed with 2 arg default" do
55
+ lambda {
56
+ pure do
57
+ def f(x, y = 99)
58
+ x + y
59
+ end
60
+ end
61
+ }.should raise_error(Pure::DefaultArgumentError)
62
+ end
63
+
64
+ it "should be parsed with non-paren 1 arg default" do
65
+ lambda {
66
+ pure do
67
+ def f x = 99
68
+ x + 44
69
+ end
70
+ end
71
+ }.should raise_error(Pure::DefaultArgumentError)
72
+ end
73
+
74
+ it "should be parsed with non-paren 2 arg 1 default" do
75
+ lambda {
76
+ pure do
77
+ def f x, y = 99
78
+ x + y
79
+ end
80
+ end
81
+ }.should raise_error(Pure::DefaultArgumentError)
82
+ end
83
+
84
+ it "should be parsed with non-paren 2 arg 2 default" do
85
+ lambda {
86
+ pure do
87
+ def f x = 77, y = 99
88
+ x + y
89
+ end
90
+ end
91
+ }.should raise_error(Pure::DefaultArgumentError)
92
+ end
93
+
94
+ it "should ignore &block" do
95
+ pure do
96
+ def f(&block)
97
+ 33
98
+ end
99
+ end.compute(4).f.should == 33
100
+ end
101
+
102
+ it "should ignore &block with 1 arg" do
103
+ pure do
104
+ def f(x, &block)
105
+ x + 44
106
+ end
107
+ end.compute(4, :x => 33).f.should == 77
108
+ end
109
+
110
+ it "should ignore &block with 2 arg" do
111
+ pure do
112
+ def f(x, y, &block)
113
+ x + 44
114
+ end
115
+ end.compute(4, :x => 33, :y => nil).f.should == 77
116
+ end
117
+
118
+ it "should ignore &block with 1 arg default" do
119
+ lambda {
120
+ pure do
121
+ def f(x = 11, &block)
122
+ x + 44
123
+ end
124
+ end
125
+ }.should raise_error(Pure::DefaultArgumentError)
126
+ end
127
+
128
+ it "should ignore no-paren &block" do
129
+ pure do
130
+ def f &block
131
+ 33
132
+ end
133
+ end.compute(4).f.should == 33
134
+ end
135
+
136
+ it "should ignore no-paren &block with 1 arg" do
137
+ pure do
138
+ def f x, &block
139
+ x + 44
140
+ end
141
+ end.compute(4, :x => 33).f.should == 77
142
+ end
143
+
144
+ it "should ignore no-paren &block with 2 arg" do
145
+ pure do
146
+ def f x, y, &block
147
+ x + 44
148
+ end
149
+ end.compute(4, :x => 33, :y => nil).f.should == 77
150
+ end
151
+
152
+ it "should ignore no-paren &block with 1 arg default" do
153
+ lambda {
154
+ pure do
155
+ def f x = 11, &block
156
+ x + 44
157
+ end
158
+ end
159
+ }.should raise_error(Pure::DefaultArgumentError)
160
+ end
161
+
162
+ it "should have fun_name and arg_names" do
163
+ pure do
164
+ def f(x, y)
165
+ [fun_name, arg_names, x + y]
166
+ end
167
+ end.compute(:x => 11, :y => 22).f.should == [:f, [:x, :y], 33]
168
+ end
169
+
170
+ it "should have fun_name and arg_names, given 1 arg" do
171
+ pure do
172
+ def f(x)
173
+ [fun_name, arg_names, x]
174
+ end
175
+ end.compute(:x => 11).f.should == [:f, [:x], 11]
176
+ end
177
+
178
+ it "should have fun_name and arg_names, given no args" do
179
+ pure do
180
+ def f
181
+ [fun_name, arg_names]
182
+ end
183
+ end.compute.f.should == [:f, []]
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,24 @@
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
+
3
+ describe "pure" do
4
+ it "should raise error when define_method called" do
5
+ lambda {
6
+ pure do
7
+ define_method :area do |width, height|
8
+ width*height
9
+ end
10
+
11
+ def width
12
+ 5
13
+ end
14
+
15
+ def height
16
+ 7
17
+ end
18
+ end.compute :area, 3
19
+ }.should raise_error(
20
+ Pure::DefineMethodError,
21
+ %r!cannot use define_method.* at #{__FILE__}:7!
22
+ )
23
+ end
24
+ end
@@ -0,0 +1,18 @@
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
+
3
+ describe "pure" do
4
+ describe "defined inside eval" do
5
+ it "should raise an error" do
6
+ lambda {
7
+ code = %{
8
+ pure do
9
+ def f
10
+ 33
11
+ end
12
+ end
13
+ }
14
+ eval(code)
15
+ }.should raise_error(Pure::EvalError, %r!#{__FILE__}:14!)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,243 @@
1
+ require File.dirname(__FILE__) + '/pure_spec_base'
2
+
3
+ require 'jumpstart'
4
+
5
+ describe "pure" do
6
+ describe "`fun' definitions" do
7
+ it "should work with symbols" do
8
+ pure do
9
+ fun :area => [:width, :height] do |w, h|
10
+ w*h
11
+ end
12
+
13
+ fun :width => [:border] do |b|
14
+ 20 + b
15
+ end
16
+
17
+ fun :height => :border do |b|
18
+ 30 + b
19
+ end
20
+
21
+ fun :border do
22
+ 5
23
+ end
24
+ end.compute(4).area.should == (20 + 5)*(30 + 5)
25
+ end
26
+
27
+ it "should work with symbols and parens" do
28
+ pure do
29
+ fun(:area => [:width, :height]) do |w, h|
30
+ w*h
31
+ end
32
+
33
+ fun(:width => [:border]) do |b|
34
+ 20 + b
35
+ end
36
+
37
+ fun(:height => :border) do |b|
38
+ 30 + b
39
+ end
40
+
41
+ fun(:border) do
42
+ 5
43
+ end
44
+ end.compute(4).area.should == (20 + 5)*(30 + 5)
45
+ end
46
+
47
+ it "should work with mixed symbols and strings" do
48
+ pure do
49
+ fun :area => [:width, "height"] do |w, h|
50
+ w*h
51
+ end
52
+
53
+ fun "width" => [:border] do |b|
54
+ 20 + b
55
+ end
56
+
57
+ fun :height => "border" do |b|
58
+ 30 + b
59
+ end
60
+
61
+ fun :border do
62
+ 5
63
+ end
64
+ end.compute(4).area.should == (20 + 5)*(30 + 5)
65
+ end
66
+
67
+ it "should work with `def' definitions" do
68
+ pure do
69
+ fun :width do
70
+ 33
71
+ end
72
+
73
+ def height
74
+ 44
75
+ end
76
+
77
+ fun :area => [:width, :height] do |w, h|
78
+ w*h
79
+ end
80
+ end.compute(3).area.should == 33*44
81
+ end
82
+
83
+ it "should be overwritten by later `def' definitions" do
84
+ Jumpstart::Ruby.no_warnings {
85
+ pure do
86
+ fun :f do
87
+ 44
88
+ end
89
+
90
+ def f
91
+ 33
92
+ end
93
+ end.compute(10).f.should == 33
94
+ }
95
+ end
96
+
97
+ it "should overwrite earlier `def' definitions" do
98
+ Jumpstart::Ruby.no_warnings {
99
+ pure do
100
+ def f
101
+ 33
102
+ end
103
+
104
+ fun :f do
105
+ 44
106
+ end
107
+ end.compute(10).f.should == 44
108
+ }
109
+ end
110
+
111
+ it "should support splat in block args" do
112
+ pure do
113
+ fun :area => [:width, :height] do |*a|
114
+ a[0]*a[1]
115
+ end
116
+
117
+ def width
118
+ 3
119
+ end
120
+
121
+ def height
122
+ 4
123
+ end
124
+ end.compute(3).area.should == 12
125
+ end
126
+
127
+ it "should support splat with single-element array" do
128
+ pure do
129
+ name = [:f]
130
+ fun(*name) do
131
+ 33
132
+ end
133
+ end.compute(3).f.should == 33
134
+ end
135
+
136
+ it "should not preclude `def' definitions called `fun'" do
137
+ pure do
138
+ def misery(fun)
139
+ fun**2
140
+ end
141
+
142
+ def fun(a, b)
143
+ a + b
144
+ end
145
+
146
+ def a
147
+ 3
148
+ end
149
+
150
+ def b
151
+ 5
152
+ end
153
+ end.compute(3).misery.should == 64
154
+ end
155
+
156
+ it "should raise error when given hash of size != 1" do
157
+ lambda {
158
+ pure do
159
+ fun :x => 1, :y => 2 do
160
+ end
161
+ end
162
+ }.should raise_error(ArgumentError)
163
+ end
164
+
165
+ it "should raise error given more than 1 argument" do
166
+ lambda {
167
+ pure do
168
+ fun :x, :y do
169
+ end
170
+ end
171
+ }.should raise_error(ArgumentError)
172
+ end
173
+
174
+ it "should raise error with &block unless Pure::Parser::Internal is used" do
175
+ code = lambda {
176
+ pure do
177
+ fun :f, &lambda { 33 }
178
+ end.compute(4).f.should == 33
179
+ }
180
+ if Pure.parser.name == "Pure::Parser::Internal"
181
+ code.should_not raise_error
182
+ else
183
+ code.should raise_error(Pure::ParseError)
184
+ end
185
+
186
+ code = lambda {
187
+ pure do
188
+ t = lambda { 33 }
189
+ fun :f, &t
190
+ end.compute(4).f.should == 33
191
+ }
192
+ if Pure.parser.name == "Pure::Parser::Internal"
193
+ code.should_not raise_error
194
+ else
195
+ code.should raise_error(Pure::ParseError)
196
+ end
197
+ end
198
+
199
+ it "should allow function names containing any characters" do
200
+ %w[- / ? : ; . ! [ ] ( )].each { |char|
201
+ pure do
202
+ fun "f#{char}f" do
203
+ 33
204
+ end
205
+ end.compute[:"f#{char}f"].should == 33
206
+ }
207
+ end
208
+
209
+ it "should have fun_name and arg_names, given multiple args" do
210
+ pure do
211
+ fun :f => [:x, :y] do |x, y|
212
+ [fun_name, arg_names, x + y]
213
+ end
214
+ end.compute(:x => 11, :y => 22).f.should == [:f, [:x, :y], 33]
215
+ end
216
+
217
+ it "should have fun_name and fun_args, given 1 arg" do
218
+ pure do
219
+ fun :f => :x do |x|
220
+ [fun_name, arg_names, x]
221
+ end
222
+ end.compute(:x => 11).f.should == [:f, [:x], 11]
223
+ end
224
+
225
+ it "should have fun_name and fun_args, given no args" do
226
+ pure do
227
+ fun :f do
228
+ [fun_name, arg_names]
229
+ end
230
+ end.compute.f.should == [:f, []]
231
+ end
232
+
233
+ it "should not see internals of the compiler" do
234
+ lambda {
235
+ pure do
236
+ fun :f do
237
+ spec
238
+ end
239
+ end.compute.f
240
+ }.should raise_error(NameError)
241
+ end
242
+ end
243
+ end