pure 0.1.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.
@@ -0,0 +1,38 @@
1
+
2
+ class Jumpstart
3
+ #
4
+ # Mixin for lazily-evaluated attributes.
5
+ #
6
+ module LazyAttribute
7
+ #
8
+ # &block is evaluated when this attribute is requested. The same
9
+ # result is returned for subsequent calls until the attribute is
10
+ # assigned a different value.
11
+ #
12
+ def attribute(reader, &block)
13
+ writer = "#{reader}="
14
+
15
+ singleton = (class << self ; self ; end)
16
+
17
+ define_evaluated_reader = lambda { |value|
18
+ singleton.class_eval {
19
+ remove_method(reader)
20
+ define_method(reader) { value }
21
+ }
22
+ }
23
+
24
+ singleton.class_eval {
25
+ define_method(reader) {
26
+ value = block.call
27
+ define_evaluated_reader.call(value)
28
+ value
29
+ }
30
+
31
+ define_method(writer) { |value|
32
+ define_evaluated_reader.call(value)
33
+ value
34
+ }
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+
2
+ require 'rbconfig'
3
+
4
+ class Jumpstart
5
+ module Ruby
6
+ EXECUTABLE = lambda {
7
+ name = File.join(
8
+ Config::CONFIG["bindir"],
9
+ Config::CONFIG["RUBY_INSTALL_NAME"]
10
+ )
11
+
12
+ if Config::CONFIG["host"] =~ %r!(mswin|cygwin|mingw)! and
13
+ File.basename(name) !~ %r!\.(exe|com|bat|cmd)\Z!i
14
+ name + Config::CONFIG["EXEEXT"]
15
+ else
16
+ name
17
+ end
18
+ }.call
19
+
20
+ class << self
21
+ def run(*args)
22
+ cmd = [EXECUTABLE, *args]
23
+ unless system(*cmd)
24
+ cmd_str = cmd.map { |t| "'#{t}'" }.join(", ")
25
+ raise "system(#{cmd_str}) failed with status #{$?.exitstatus}"
26
+ end
27
+ end
28
+
29
+ def with_warnings(value = true)
30
+ previous = $VERBOSE
31
+ $VERBOSE = value
32
+ begin
33
+ yield
34
+ ensure
35
+ $VERBOSE = previous
36
+ end
37
+ end
38
+
39
+ def no_warnings(&block)
40
+ with_warnings(false, &block)
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,85 @@
1
+
2
+ require 'rbconfig'
3
+ require 'fileutils'
4
+ require 'find'
5
+
6
+ class Jumpstart
7
+ class SimpleInstaller
8
+ def initialize
9
+ dest_root = Config::CONFIG["sitelibdir"]
10
+ sources = []
11
+ Find.find("./lib") { |source|
12
+ if install_file?(source)
13
+ sources << source
14
+ end
15
+ }
16
+ @spec = sources.inject(Array.new) { |acc, source|
17
+ if source == "./lib"
18
+ acc
19
+ else
20
+ dest = File.join(dest_root, source.sub(%r!\A\./lib!, ""))
21
+
22
+ install = lambda {
23
+ if File.directory?(source)
24
+ unless File.directory?(dest)
25
+ puts "mkdir #{dest}"
26
+ FileUtils.mkdir(dest)
27
+ end
28
+ else
29
+ puts "install #{source} --> #{dest}"
30
+ FileUtils.install(source, dest)
31
+ end
32
+ }
33
+
34
+ uninstall = lambda {
35
+ if File.directory?(source)
36
+ if File.directory?(dest)
37
+ puts "rmdir #{dest}"
38
+ FileUtils.rmdir(dest)
39
+ end
40
+ else
41
+ if File.file?(dest)
42
+ puts "rm #{dest}"
43
+ FileUtils.rm(dest)
44
+ end
45
+ end
46
+ }
47
+
48
+ acc << {
49
+ :source => source,
50
+ :dest => dest,
51
+ :install => install,
52
+ :uninstall => uninstall,
53
+ }
54
+ end
55
+ }
56
+ end
57
+
58
+ def install_file?(source)
59
+ File.directory?(source) or
60
+ (File.file?(source) and File.extname(source) == ".rb")
61
+ end
62
+
63
+ def install
64
+ @spec.each { |entry|
65
+ entry[:install].call
66
+ }
67
+ end
68
+
69
+ def uninstall
70
+ @spec.reverse.each { |entry|
71
+ entry[:uninstall].call
72
+ }
73
+ end
74
+
75
+ def run(args = ARGV)
76
+ if args.empty?
77
+ install
78
+ elsif args.size == 1 and args.first == "--uninstall"
79
+ uninstall
80
+ else
81
+ raise "unrecognized arguments: #{args.inspect}"
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,3 @@
1
+ $LOAD_PATH.unshift "devel"
2
+ require 'jumpstart/simple_installer'
3
+ Jumpstart::SimpleInstaller.new.run
@@ -0,0 +1,19 @@
1
+
2
+ require 'pure/pure_private/creator'
3
+ require 'pure/pure_private/extractor'
4
+
5
+ module Pure
6
+ PURE_VERSION = "0.1.0"
7
+
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,27 @@
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
@@ -0,0 +1,48 @@
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
@@ -0,0 +1,32 @@
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
@@ -0,0 +1,79 @@
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
@@ -0,0 +1,95 @@
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