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,27 +0,0 @@
1
-
2
- require 'pure/pure_private/util'
3
- require 'pure/pure_private/singleton_features'
4
-
5
- module Pure
6
- module PurePrivate
7
- module Creator
8
- class << self
9
- include Util
10
-
11
- def create_module(&block)
12
- mod = Module.new
13
- fun_mod = Module.new
14
- singleton_class_of(mod).module_eval {
15
- include SingletonFeatures
16
- @fun_mod = fun_mod
17
- }
18
- mod.module_eval(&block)
19
- mod.module_eval {
20
- include fun_mod
21
- }
22
- mod
23
- end
24
- end
25
- end
26
- end
27
- end
@@ -1,48 +0,0 @@
1
-
2
- require 'pure/pure_private/function_database'
3
- require 'pure/pure_private/error'
4
- require 'comp_tree'
5
-
6
- module Pure
7
- module PurePrivate
8
- module Driver
9
- include FunctionDatabase
10
-
11
- module_function
12
-
13
- def build_and_compute(mod, root, num_threads, &block)
14
- begin
15
- CompTree.build do |driver|
16
- mod.ancestors.each { |ancestor|
17
- if defs = FUNCTION_DATABASE[ancestor]
18
- defs.each_pair { |function_name, spec|
19
- existing_node = driver.nodes[function_name]
20
- if existing_node.nil? or existing_node.function.nil?
21
- final_spec = spec.merge(:module => ancestor)
22
- node = driver.define(function_name, *spec[:args])
23
- node.function = yield function_name, final_spec
24
- end
25
- }
26
- end
27
- }
28
- driver.compute(root, num_threads)
29
- end
30
- rescue CompTree::NoFunctionError => exception
31
- raise PurePrivate::NoFunctionError, exception.message
32
- rescue CompTree::ArgumentError => exception
33
- raise PurePrivate::ArgumentError, exception.message
34
- end
35
- end
36
-
37
- def create_instance_and_compute(mod, root, opts)
38
- num_threads = (opts.is_a?(Hash) ? opts[:threads] : opts).to_i
39
- instance = Object.new.extend(mod)
40
- build_and_compute(mod, root, num_threads) { |function_name, spec|
41
- lambda { |*args|
42
- instance.send(function_name, *args)
43
- }
44
- }
45
- end
46
- end
47
- end
48
- end
@@ -1,32 +0,0 @@
1
-
2
- module Pure
3
- module PurePrivate
4
- class Error < StandardError
5
- end
6
-
7
- class NoFunctionError < Error
8
- end
9
-
10
- class ParseError < Error
11
- end
12
-
13
- class ArgumentError < Error
14
- end
15
-
16
- class SplatError < ParseError
17
- def initialize(file, line)
18
- super()
19
- @file = file
20
- @line = line
21
- end
22
-
23
- def message
24
- "cannot use splat (*) argument in a pure function defined with `def' " +
25
- "at #{@file}:#{@line}"
26
- end
27
- end
28
-
29
- class NotImplementedError < Error
30
- end
31
- end
32
- end
@@ -1,79 +0,0 @@
1
-
2
- require 'pure/pure_private/error'
3
- require 'pure/pure_private/util'
4
-
5
- module Pure
6
- module PurePrivate
7
- module Extractor
8
- HAS_METHOD_PARAMETERS = Method.instance_methods.include?(:parameters)
9
-
10
- DEFAULT_PARSER = (
11
- if HAS_METHOD_PARAMETERS
12
- nil
13
- elsif RUBY_VERSION >= "1.9"
14
- "ripper"
15
- else
16
- "ruby_parser"
17
- end
18
- )
19
-
20
- @parser = nil
21
- @engine = nil
22
- @cache = Hash.new { |hash, key| hash[key] = Hash.new }
23
-
24
- class << self
25
- include Util
26
-
27
- def extract(mod, method_name, backtrace)
28
- file, line = file_line(backtrace.first)
29
- if @parser.nil? and HAS_METHOD_PARAMETERS
30
- if method_name == :fun
31
- Hash.new
32
- else
33
- {
34
- :name => method_name,
35
- :args => mod.instance_method(method_name).parameters.map {
36
- |type, name|
37
- raise SplatError.new(file, line) if type == :rest
38
- name
39
- },
40
- }
41
- end
42
- else
43
- if @parser.nil?
44
- self.parser = DEFAULT_PARSER
45
- end
46
- defs = @cache[@parser][file] || (
47
- @cache[@parser][file] = @engine.new(file).run
48
- )
49
- spec = defs[line]
50
- unless spec and spec[:name] and spec[:name] == method_name
51
- raise PurePrivate::ParseError,
52
- "failure parsing `#{method_name}' at #{file}:#{line}"
53
- end
54
- spec.merge(:file => file, :line => line)
55
- end
56
- end
57
-
58
- def parser=(parser_name)
59
- if parser_name.nil?
60
- @engine = nil
61
- else
62
- require parser_name
63
- begin
64
- engine_name = "extractor_#{parser_name}"
65
- require "pure/pure_private/#{engine_name}"
66
- @engine = PurePrivate.const_get(to_camel_case(engine_name))
67
- rescue LoadError
68
- raise PurePrivate::NotImplementedError,
69
- "parser not supported: #{parser_name}"
70
- end
71
- end
72
- @parser = parser_name
73
- end
74
-
75
- attr_reader :parser
76
- end
77
- end
78
- end
79
- end
@@ -1,95 +0,0 @@
1
-
2
- require 'ripper'
3
-
4
- module Pure
5
- module PurePrivate
6
- class ExtractorRipper
7
- def initialize(file)
8
- @file = file
9
- @defs = Hash.new
10
- end
11
-
12
- def run
13
- process(Ripper.sexp(File.read(@file)))
14
- @defs
15
- end
16
-
17
- def process_def(sexp)
18
- if sexp[0] == :def
19
- name = sexp[1][1].to_sym
20
- line = sexp[1][2][0]
21
- params = (
22
- case sexp[2].first
23
- when :params
24
- sexp[2]
25
- when :paren
26
- sexp[2][1]
27
- else
28
- raise PurePrivate::ParseError,
29
- "unforeseen `def' syntax at #{@file}:#{line}"
30
- end
31
- )
32
- if params.any? { |t| t and t[0] == :rest_param }
33
- raise SplatError.new(@file, line)
34
- end
35
- args = (
36
- if params[1].nil?
37
- []
38
- else
39
- params[1].map { |t| t[1].to_sym }
40
- end
41
- )
42
- @defs[line] = {
43
- :name => name,
44
- :args => args,
45
- :sexp => sexp,
46
- }
47
- true
48
- else
49
- false
50
- end
51
- end
52
-
53
- def process_fun(sexp)
54
- if sexp[0] == :method_add_block and sexp[1].is_a?(Array)
55
- line = (
56
- if sexp[1][0] == :command and
57
- sexp[1][1].is_a?(Array) and
58
- sexp[1][1][1] == "fun"
59
- sexp[1][1][2][0]
60
- elsif sexp[1][0] == :method_add_arg and
61
- sexp[1][1].is_a?(Array) and
62
- sexp[1][1][0] == :fcall and
63
- sexp[1][1][1].is_a?(Array) and
64
- sexp[1][1][1][1] == "fun"
65
- sexp[1][1][1][2][0]
66
- else
67
- nil
68
- end
69
- )
70
- if line
71
- @defs[line] = {
72
- :name => :fun,
73
- :sexp => sexp[2],
74
- }
75
- true
76
- else
77
- false
78
- end
79
- else
80
- false
81
- end
82
- end
83
-
84
- def process(sexp)
85
- if sexp.is_a? Array
86
- process_def(sexp) or process_fun(sexp) or (
87
- sexp.each { |sub_sexp|
88
- process(sub_sexp)
89
- }
90
- )
91
- end
92
- end
93
- end
94
- end
95
- end
@@ -1,47 +0,0 @@
1
-
2
- require 'ruby_parser'
3
- require 'sexp_processor'
4
-
5
- module Pure
6
- module PurePrivate
7
- class ExtractorRubyParser < SexpProcessor
8
- def initialize(file)
9
- super()
10
- @file = file
11
- @defs = Hash.new
12
- end
13
-
14
- def run
15
- process(RubyParser.new.parse(File.read(@file)))
16
- @defs
17
- end
18
-
19
- def process(sexp)
20
- if sexp[0] == :defn
21
- name = sexp[1]
22
- args = sexp[2].to_a[1..-1]
23
- if args.any? { |arg| arg.to_s =~ %r!\A\*! }
24
- raise SplatError.new(@file, sexp.line)
25
- end
26
- @defs[sexp.line] = {
27
- :name => name,
28
- :args => args,
29
- :sexp => sexp.dup,
30
- }
31
- sexp.clear
32
- elsif sexp[0] == :iter and
33
- sexp[1][0] == :call and
34
- sexp[1][1] == nil and
35
- sexp[1][2] == :fun
36
- @defs[sexp[1].line] = {
37
- :name => :fun,
38
- :sexp => [sexp[2], sexp[3]],
39
- }
40
- sexp.clear
41
- else
42
- super
43
- end
44
- end
45
- end
46
- end
47
- end
@@ -1,10 +0,0 @@
1
-
2
- module Pure
3
- module PurePrivate
4
- module FunctionDatabase
5
- FUNCTION_DATABASE = Hash.new { |hash, key|
6
- hash[key] = Hash.new
7
- }
8
- end
9
- end
10
- end
@@ -1,67 +0,0 @@
1
-
2
- require 'pure/pure_private/extractor'
3
- require 'pure/pure_private/function_database'
4
- require 'pure/pure_private/driver'
5
- require 'pure/pure_private/error'
6
-
7
- module Pure
8
- module PurePrivate
9
- module SingletonFeatures
10
- include FunctionDatabase
11
-
12
- def compute(root, opts)
13
- Driver.create_instance_and_compute(self, root, opts)
14
- end
15
-
16
- def fun(*args, &block)
17
- fun_mod = class << self ; @fun_mod ; end
18
- node_name, child_names = (
19
- if args.size == 1
20
- arg = args.first
21
- if arg.is_a? Hash
22
- unless arg.size == 1
23
- raise PurePrivate::ArgumentError,
24
- "`fun' given hash of size != 1"
25
- end
26
- arg.to_a.first
27
- else
28
- [arg, []]
29
- end
30
- else
31
- raise PurePrivate::ArgumentError,
32
- "wrong number of arguments (\#{args.size} for 1)"
33
- end
34
- )
35
- child_syms = (
36
- if child_names.is_a? Enumerable
37
- child_names.map { |t| t.to_sym }
38
- else
39
- [child_names.to_sym]
40
- end
41
- )
42
- node_sym = node_name.to_sym
43
- fun_mod.module_eval {
44
- define_method(node_sym, &block)
45
- }
46
- spec = Extractor.extract(fun_mod, :fun, caller)
47
- FUNCTION_DATABASE[fun_mod][node_sym] = spec.merge(
48
- :name => node_sym,
49
- :args => child_syms,
50
- :origin => :fun
51
- )
52
- nil
53
- end
54
-
55
- def method_added(function_name)
56
- FUNCTION_DATABASE[self][function_name] = (
57
- Extractor.extract(self, function_name, caller).merge(:origin => :def)
58
- )
59
- end
60
-
61
- def define_method(*args, &block)
62
- raise PurePrivate::NotImplementedError,
63
- "define_method called (use the `fun' method instead)"
64
- end
65
- end
66
- end
67
- end
@@ -1,23 +0,0 @@
1
-
2
- module Pure
3
- module PurePrivate
4
- module Util
5
- module_function
6
-
7
- def file_line(backtrace)
8
- file, line = backtrace.match(%r!\A(.*?):(\d+)!).captures
9
- return file, line.to_i
10
- end
11
-
12
- def singleton_class_of(obj)
13
- class << obj
14
- self
15
- end
16
- end
17
-
18
- def to_camel_case(str)
19
- str.split('_').map { |t| t.capitalize }.join
20
- end
21
- end
22
- end
23
- end
@@ -1,38 +0,0 @@
1
- require File.dirname(__FILE__) + "/common"
2
-
3
- describe "basic computation" do
4
- before :all do
5
- @mod = pure do
6
- def area(width, height)
7
- width*height
8
- end
9
-
10
- def width(border)
11
- 20 + border
12
- end
13
-
14
- def height(border)
15
- 30 + border
16
- end
17
-
18
- def border
19
- 5
20
- end
21
- end
22
- end
23
-
24
- max_threads = 50
25
-
26
- it "should run using compute(:x, :threads => n) syntax" do
27
- (1..max_threads).each { |n|
28
- @mod.compute(:area, :threads => n).should == (20 + 5)*(30 + 5)
29
- }
30
- end
31
-
32
- it "should run using compute(:x, n) syntax" do
33
- (1..max_threads).each { |n|
34
- @mod.compute(:area, n).should == (20 + 5)*(30 + 5)
35
- }
36
- end
37
- end
38
-