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
data/install.rb CHANGED
@@ -1,3 +1,2 @@
1
- $LOAD_PATH.unshift "devel"
2
- require 'jumpstart/simple_installer'
1
+ load './devel/jumpstart.rb'
3
2
  Jumpstart::SimpleInstaller.new.run
@@ -1,19 +1,41 @@
1
+ #
2
+ # Copyright (c) 2008, 2009 James M. Lawrence. All rights reserved.
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person
5
+ # obtaining a copy of this software and associated documentation files
6
+ # (the "Software"), to deal in the Software without restriction,
7
+ # including without limitation the rights to use, copy, modify, merge,
8
+ # publish, distribute, sublicense, and/or sell copies of the Software,
9
+ # and to permit persons to whom the Software is furnished to do so,
10
+ # subject to the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19
+ # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20
+ # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
+ # SOFTWARE.
23
+ #
1
24
 
2
- require 'pure/pure_private/creator'
3
- require 'pure/pure_private/extractor'
25
+ require 'comp_tree'
4
26
 
5
- module Pure
6
- PURE_VERSION = "0.1.0"
27
+ require 'pure/version'
28
+ require 'pure/error'
29
+ require 'pure/util'
30
+ require 'pure/extractor'
31
+ require 'pure/extracted_functions'
32
+ require 'pure/bundled_plugin'
33
+ require 'pure/bundled_parsers'
34
+ require 'pure/driver'
35
+ require 'pure/native_worker'
36
+ require 'pure/delegate'
37
+ require 'pure/dsl_definition'
38
+ require 'pure/names'
39
+ require 'pure/pure_module'
40
+ require 'pure/pure'
7
41
 
8
- module_function
9
-
10
- def pure(&block)
11
- PurePrivate::Creator.create_module(&block)
12
- end
13
-
14
- class << self
15
- [:parser, :parser=].each { |name|
16
- define_method name, &PurePrivate::Extractor.method(name)
17
- }
18
- end
19
- end
@@ -0,0 +1,4 @@
1
+
2
+ module Pure
3
+ BundledParsers = BundledPlugin.new("pure", "parser", NoParserError)
4
+ end
@@ -0,0 +1,49 @@
1
+
2
+ module Pure
3
+ class BundledPlugin
4
+ def initialize(project_name, plugin_name, empty_error)
5
+ @project_name = project_name
6
+ @plugin_name = plugin_name
7
+ @empty_error = empty_error
8
+ end
9
+
10
+ def files
11
+ result = Dir[File.dirname(__FILE__) + "/#{@plugin_name}/*.rb"].map {
12
+ |file|
13
+ file[%r!(#{@project_name}/#{@plugin_name}/.*)\.rb\Z!, 1]
14
+ }
15
+ result.sort # rcov workaround
16
+ end
17
+
18
+ def fetch(file)
19
+ begin
20
+ require file
21
+ [@project_name, @plugin_name, File.basename(file)].inject(Object) {
22
+ |acc, name|
23
+ acc.const_get(Util.to_camel_case(name))
24
+ }
25
+ rescue LoadError
26
+ nil
27
+ end
28
+ end
29
+
30
+ def available
31
+ files.map { |file|
32
+ [file, fetch(file)]
33
+ }.reject { |file, object|
34
+ object.nil?
35
+ }.inject(Hash.new) { |acc, (file, object)|
36
+ acc.merge!(file => object)
37
+ }
38
+ end
39
+
40
+ def find_default
41
+ files.each { |file|
42
+ if found = fetch(file)
43
+ return found
44
+ end
45
+ }
46
+ raise @empty_error
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,63 @@
1
+ #
2
+ # This file is self-contained, intended for use by an external server.
3
+ #
4
+ # It is not included by the top-level pure.rb.
5
+ #
6
+
7
+ require 'pure/version'
8
+ require 'pure/names'
9
+ require 'ruby2ruby'
10
+
11
+ module Pure
12
+ module Compiler #:nodoc:
13
+ class RubyParser #:nodoc:
14
+ def initialize
15
+ @ruby2ruby = Ruby2Ruby.new
16
+ end
17
+
18
+ #
19
+ # Compiles and evaluates a function spec with arg values.
20
+ #
21
+ def evaluate_function(spec, *args)
22
+ compile_function(spec).send(spec[:name], *args)
23
+ end
24
+
25
+ #
26
+ # Compiles a function spec extracted by
27
+ # Pure::Parser::RubyParser.
28
+ #
29
+ # Returns an object which responds to spec[:name].
30
+ #
31
+ def compile_function(spec)
32
+ sexp = (
33
+ if spec[:origin] == :fun
34
+ fun_to_define_method(spec[:name], spec[:code])
35
+ else
36
+ spec[:code]
37
+ end
38
+ )
39
+
40
+ instance = Names.new(spec[:name], spec[:args])
41
+ Thread.current[:pure_compiler_input] = @ruby2ruby.process(sexp)
42
+
43
+ # use singleton to hide locals
44
+ class << instance
45
+ eval(Thread.current[:pure_compiler_input])
46
+ end
47
+
48
+ instance
49
+ end
50
+
51
+ #
52
+ # Code-transform `fun' definitions into `define_method' definitions.
53
+ #
54
+ def fun_to_define_method(name, sexp)
55
+ s(:iter,
56
+ s(:call, nil, :define_method, s(:arglist, s(:lit, name.to_sym))),
57
+ sexp[2],
58
+ sexp[3]
59
+ )
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,16 @@
1
+
2
+ module Pure
3
+ class Delegate
4
+ def initialize(driver)
5
+ (class << self ; self ; end).class_eval do
6
+ driver.each_function_name { |name|
7
+ define_method name do ||
8
+ driver.compute(name)
9
+ end
10
+ }
11
+ end
12
+ end
13
+
14
+ alias_method :[], :send
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+
2
+ module Pure
3
+ class Driver
4
+ def initialize(worker_class, mod, num_parallel, overrides)
5
+ @worker = worker_class.new
6
+ @worker.define_function_begin(mod, num_parallel)
7
+ @driver = CompTree.build do |driver|
8
+ overrides.each_pair { |name, value|
9
+ driver.define(name.to_sym) { value }
10
+ }
11
+ mod.each_function { |name, spec|
12
+ node = driver.nodes[name]
13
+ unless node and node.function
14
+ function = @worker.define_function(spec)
15
+ driver.define(name, *spec[:args], &function)
16
+ end
17
+ }
18
+ driver
19
+ end
20
+ @worker.define_function_end
21
+ end
22
+
23
+ def compute(root)
24
+ @driver.compute(root, @worker.num_parallel)
25
+ rescue CompTree::NoFunctionError => exception
26
+ raise Pure::NoFunctionError.new(exception.node_name)
27
+ end
28
+
29
+ def each_function_name(&block)
30
+ @driver.nodes.each_key(&block)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,2 @@
1
+ require 'pure'
2
+ include Pure::DSL
@@ -0,0 +1,11 @@
1
+
2
+ module Pure
3
+ module DSL
4
+ #
5
+ # Alias of Pure.define
6
+ #
7
+ def pure(*args, &block)
8
+ Pure.define(*args, &block)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,89 @@
1
+
2
+ module Pure
3
+ # Base class for Pure errors.
4
+ class Error < StandardError
5
+ end
6
+
7
+ # No parser found.
8
+ class NoParserError < Error
9
+ def initialize #:nodoc:
10
+ super("no parser found")
11
+ end
12
+ end
13
+
14
+ # A missing function was encountered during a computation.
15
+ class NoFunctionError < Error
16
+ attr_reader :function_name #:nodoc:
17
+
18
+ def initialize(function_name) #:nodoc:
19
+ @function_name = function_name
20
+ super(custom_message)
21
+ end
22
+
23
+ def custom_message #:nodoc:
24
+ "no function named `#{function_name}'"
25
+ end
26
+ end
27
+
28
+ # Base class for errors found during extraction.
29
+ class ExtractionTimeError < Error
30
+ attr_reader :file, :line #:nodoc:
31
+
32
+ def initialize(file, line) #:nodoc:
33
+ @file, @line = file, line
34
+ super(custom_message + " at #{@file}:#{@line}")
35
+ end
36
+ end
37
+
38
+ class ParseError < ExtractionTimeError
39
+ end
40
+
41
+ class ParseEntityError < ParseError
42
+ attr_reader :entity
43
+
44
+ def initialize(file, line, entity)
45
+ @entity = entity
46
+ super(file, line)
47
+ end
48
+
49
+ def custom_message
50
+ "failed to parse `#{entity}'"
51
+ end
52
+ end
53
+
54
+ # The parser could not extract a method definition.
55
+ class ParseMethodError < ParseEntityError
56
+ end
57
+
58
+ # Base class for breaches of pure function restrictions.
59
+ class RestrictionError < ExtractionTimeError
60
+ end
61
+
62
+ # A *splat argument was present in a pure function defined with `def'.
63
+ class SplatError < RestrictionError
64
+ def custom_message #:nodoc:
65
+ "cannot use splat argument (*) in pure function defined with `def'"
66
+ end
67
+ end
68
+
69
+ # A pure function had a default argument.
70
+ class DefaultArgumentError < RestrictionError
71
+ def custom_message #:nodoc:
72
+ "cannot use default argument in pure function"
73
+ end
74
+ end
75
+
76
+ # +define_method+ called inside a pure module.
77
+ class DefineMethodError < RestrictionError
78
+ def custom_message #:nodoc:
79
+ "cannot use define_method in a pure module (use `fun' instead)"
80
+ end
81
+ end
82
+
83
+ # Attempt to create a pure module inside +eval+.
84
+ class EvalError < RestrictionError
85
+ def custom_message #:nodoc:
86
+ "cannot define a pure module inside eval (write it to a file instead)"
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,11 @@
1
+
2
+ module Pure
3
+ #
4
+ # ExtractedFunctions[parser][pure_module][function_name] = function_data
5
+ #
6
+ ExtractedFunctions = Hash.new { |hash, key|
7
+ hash[key] = Hash.new { |h, k|
8
+ h[k] = Hash.new
9
+ }
10
+ }
11
+ end
@@ -0,0 +1,59 @@
1
+
2
+ module Pure
3
+ module Extractor
4
+ module_function
5
+
6
+ def extract(mod, method_name, backtrace)
7
+ file, line = Util.file_line(backtrace.first)
8
+ if file == "(eval)"
9
+ eval_error(backtrace)
10
+ end
11
+ mod.parser.extract(mod, method_name, file, line)
12
+ end
13
+
14
+ def eval_error(backtrace)
15
+ backtrace.each_with_index { |desc, index|
16
+ if desc =~ %r!:in \`eval\'\Z!
17
+ file, line = Util.file_line(backtrace[index + 1])
18
+ raise EvalError.new(file, line)
19
+ end
20
+ }
21
+ end
22
+
23
+ def record_function(mod, origin, name, args, backtrace) #:nodoc:
24
+ spec = ExtractedFunctions[mod.parser][mod][name] = (
25
+ case origin
26
+ when :def
27
+ #
28
+ # For `def' definitions, function and argument names are
29
+ # determined at parse-time.
30
+ #
31
+ extract(mod, name, backtrace)
32
+ when :fun
33
+ #
34
+ # For `fun' definitions, function and argument names are
35
+ # determined at run-time.
36
+ #
37
+ # Use the __fun flag to verify the parser's discovery of a
38
+ # `fun' call.
39
+ #
40
+ extract(mod, :__fun, backtrace).
41
+ merge(:name => name, :args => args)
42
+ end
43
+ ).merge(
44
+ :origin => origin,
45
+ :parser => mod.parser.name
46
+ )
47
+
48
+ if origin == :def and spec[:splat]
49
+ raise SplatError.new(spec[:file], spec[:line])
50
+ end
51
+
52
+ if spec[:default]
53
+ raise DefaultArgumentError.new(spec[:file], spec[:line])
54
+ end
55
+
56
+ spec
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,9 @@
1
+
2
+ module Pure
3
+ class Names
4
+ attr_reader :fun_name, :arg_names
5
+ def initialize(fun_name, arg_names)
6
+ @fun_name, @arg_names = fun_name, arg_names
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+
2
+ module Pure
3
+ class NativeWorker #:nodoc:
4
+ attr_reader :num_parallel
5
+
6
+ def define_function_begin(pure_module, num_parallel)
7
+ @num_parallel = num_parallel || self.class.num_parallel
8
+ @class = Class.new Names do
9
+ include pure_module
10
+ end
11
+ end
12
+
13
+ def define_function(spec)
14
+ lambda { |*args|
15
+ @class.new(spec[:name], spec[:args]).send(spec[:name], *args)
16
+ }
17
+ end
18
+
19
+ def define_function_end
20
+ end
21
+
22
+ class << self
23
+ attr_accessor :num_parallel
24
+ end
25
+ @num_parallel = 1
26
+ end
27
+ end