pure 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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