rewrite 0.0.1 → 0.0.2

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.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2008-06-15
2
+
3
+ * 1 tiny enhancement:
4
+ * Initial pre-release of a preview of an alpha of an undocumented proof-of-concept
data/License.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Reginald Braithwaite
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Manifest.txt ADDED
@@ -0,0 +1,37 @@
1
+ History.txt
2
+ License.txt
3
+ Manifest.txt
4
+ PostInstall.txt
5
+ README.txt
6
+ Rakefile
7
+ config/hoe.rb
8
+ config/requirements.rb
9
+ lib/rewrite.rb
10
+ lib/rewrite/def_var.rb
11
+ lib/rewrite/evaluation_strategies.rb
12
+ lib/rewrite/prelude.rb
13
+ lib/rewrite/prelude/andand.rb
14
+ lib/rewrite/prelude/called_by_name.rb
15
+ lib/rewrite/variables.rb
16
+ lib/rewrite/version.rb
17
+ lib/rewrite/with.rb
18
+ script/console
19
+ script/destroy
20
+ script/generate
21
+ script/txt2html
22
+ setup.rb
23
+ tasks/deployment.rake
24
+ tasks/environment.rake
25
+ tasks/website.rake
26
+ test/test_andand.rb
27
+ test/test_call_by_name.rb
28
+ test/test_call_by_thunk.rb
29
+ test/test_helper.rb
30
+ test/test_rewriter_helpers.rb
31
+ test/test_syntax_let.rb
32
+ test/test_with.rb
33
+ website/index.html
34
+ website/index.txt
35
+ website/javascripts/rounded_corners_lite.inc.js
36
+ website/stylesheets/screen.css
37
+ website/template.html.erb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on rewrite, see http://rewrite.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.txt ADDED
@@ -0,0 +1,46 @@
1
+ = rewrite
2
+
3
+ * http://rewrite.rubyforge.org
4
+
5
+ == DESCRIPTION:
6
+
7
+ * Adds certain features to Ruby programs using sexp rewriting rather than open classes.
8
+
9
+ == FEATURES:
10
+
11
+ * Andand
12
+ * Call by name
13
+
14
+ == REQUIREMENTS:
15
+
16
+ * Parse_Tree
17
+ * Ruby2Ruby
18
+
19
+ == INSTALL:
20
+
21
+ * sudo gem install rewrite
22
+
23
+ == LICENSE:
24
+
25
+ (The MIT License)
26
+
27
+ Copyright (c) 2008 Reg Braithwaite
28
+
29
+ Permission is hereby granted, free of charge, to any person obtaining
30
+ a copy of this software and associated documentation files (the
31
+ 'Software'), to deal in the Software without restriction, including
32
+ without limitation the rights to use, copy, modify, merge, publish,
33
+ distribute, sublicense, and/or sell copies of the Software, and to
34
+ permit persons to whom the Software is furnished to do so, subject to
35
+ the following conditions:
36
+
37
+ The above copyright notice and this permission notice shall be
38
+ included in all copies or substantial portions of the Software.
39
+
40
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
41
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
42
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
43
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
44
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
45
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
46
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'config/requirements'
2
+ require 'config/hoe' # setup Hoe + all gem configuration
3
+
4
+ Dir['tasks/**/*.rake'].each { |rake| load rake }
data/config/hoe.rb ADDED
@@ -0,0 +1,74 @@
1
+ require 'rewrite/version'
2
+
3
+ AUTHOR = 'Reg Braithwaite' # can also be an array of Authors
4
+ EMAIL = "raganwald+rewrite@gmail.com"
5
+ DESCRIPTION = "Syntactic metaprogramming for Ruby"
6
+ GEM_NAME = 'rewrite' # what ppl will type to install your gem
7
+ RUBYFORGE_PROJECT = 'rewrite' # The unix name for your project
8
+ HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
9
+ DOWNLOAD_PATH = "http://rubyforge.org/projects/#{RUBYFORGE_PROJECT}"
10
+ EXTRA_DEPENDENCIES = [
11
+ ['ruby2ruby', '>= 1.1.9'],
12
+ ['parse_tree', '>= 2.2.0']
13
+ ] # An array of rubygem dependencies [name, version]
14
+
15
+ @config_file = "~/.rubyforge/user-config.yml"
16
+ @config = nil
17
+ RUBYFORGE_USERNAME = "raganwald"
18
+ def rubyforge_username
19
+ unless @config
20
+ begin
21
+ @config = YAML.load(File.read(File.expand_path(@config_file)))
22
+ rescue
23
+ puts <<-EOS
24
+ ERROR: No rubyforge config file found: #{@config_file}
25
+ Run 'rubyforge setup' to prepare your env for access to Rubyforge
26
+ - See http://newgem.rubyforge.org/rubyforge.html for more details
27
+ EOS
28
+ exit
29
+ end
30
+ end
31
+ RUBYFORGE_USERNAME.replace @config["username"]
32
+ end
33
+
34
+
35
+ REV = nil
36
+ # UNCOMMENT IF REQUIRED:
37
+ # REV = YAML.load(`svn info`)['Revision']
38
+ VERS = Rewrite::VERSION::STRING + (REV ? ".#{REV}" : "")
39
+ RDOC_OPTS = ['--quiet', '--title', 'rewrite documentation',
40
+ "--opname", "index.html",
41
+ "--line-numbers",
42
+ "--main", "README",
43
+ "--inline-source"]
44
+
45
+ class Hoe
46
+ def extra_deps
47
+ @extra_deps.reject! { |x| Array(x).first == 'hoe' }
48
+ @extra_deps
49
+ end
50
+ end
51
+
52
+ # Generate all the Rake tasks
53
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
54
+ $hoe = Hoe.new(GEM_NAME, VERS) do |p|
55
+ p.developer(AUTHOR, EMAIL)
56
+ p.description = DESCRIPTION
57
+ p.summary = DESCRIPTION
58
+ p.url = HOMEPATH
59
+ p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
60
+ p.test_globs = ["test/**/test_*.rb"]
61
+ p.clean_globs |= ['**/.*.sw?', '*.gem', '.config', '**/.DS_Store'] #An array of file patterns to delete on clean.
62
+
63
+ # == Optional
64
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
65
+ #p.extra_deps = EXTRA_DEPENDENCIES
66
+
67
+ #p.spec_extras = {} # A hash of extra values to set in the gemspec.
68
+ end
69
+
70
+ CHANGES = $hoe.paragraphs_of('History.txt', 0..1).join("\\n\\n")
71
+ PATH = (RUBYFORGE_PROJECT == GEM_NAME) ? RUBYFORGE_PROJECT : "#{RUBYFORGE_PROJECT}/#{GEM_NAME}"
72
+ $hoe.remote_rdoc_dir = File.join(PATH.gsub(/^#{RUBYFORGE_PROJECT}\/?/,''), 'rdoc')
73
+ $hoe.rsync_args = '-av --delete --ignore-errors'
74
+ $hoe.spec.post_install_message = File.open(File.dirname(__FILE__) + "/../PostInstall.txt").read rescue ""
@@ -0,0 +1,15 @@
1
+ require 'fileutils'
2
+ include FileUtils
3
+
4
+ require 'rubygems'
5
+ %w[rake hoe newgem rubigen].each do |req_gem|
6
+ begin
7
+ require req_gem
8
+ rescue LoadError
9
+ puts "This Rakefile requires the '#{req_gem}' RubyGem."
10
+ puts "Installation: gem install #{req_gem} -y"
11
+ exit
12
+ end
13
+ end
14
+
15
+ $:.unshift(File.join(File.dirname(__FILE__), %w[.. lib]))
@@ -0,0 +1,63 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+ require 'parse_tree'
5
+ require 'sexp_processor'
6
+
7
+ module Rewrite
8
+
9
+ class DefVar
10
+
11
+ def initialize(sym, sexp)
12
+ @sym, @sexp = sym, sexp
13
+ end
14
+
15
+ def process(sexp)
16
+ if sexp.length >= 3 && sexp[0] == :call && sexp[2] == :call &&
17
+ (callee = sexp[1]) && callee.length >= 4 && callee[0] == :iter && callee[1].to_a == [:fcall, :lambda]
18
+ parameters = callee[2]
19
+ if parameters.nil?
20
+ callee[2] = s(:dasgn_curr, @sym)
21
+ s(:call,
22
+ callee,
23
+ :call,
24
+ s(:array, @sexp)
25
+ )
26
+ elsif parameters[0] == :dasgn || parameters[0] == :dasgn_curr
27
+ callee[2] = s(:masgn,
28
+ s(:array,
29
+ s(:dasgn_curr, parameters[1]),
30
+ s(:dasgn_curr, @sym)
31
+ )
32
+ )
33
+ s(:call,
34
+ callee,
35
+ :call,
36
+ sexp[3] + [ @sexp ]
37
+ )
38
+ elsif parameters[0] == :masgn
39
+ callee[2][1] << s(:dasgn_curr, @sym)
40
+ s(:call,
41
+ callee,
42
+ :call,
43
+ sexp[3] + [ @sexp ]
44
+ )
45
+ else
46
+ raise "Confused by #{Ruby2Ruby.new.process(parameters)}"
47
+ end
48
+ else
49
+ s(:call,
50
+ s(:iter,
51
+ s(:fcall, :lambda),
52
+ s(:dasgn_curr, @sym),
53
+ sexp
54
+ ),
55
+ :call,
56
+ s(:array, @sexp)
57
+ )
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -0,0 +1,86 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+ require 'parse_tree'
5
+ require 'sexp_processor'
6
+
7
+ module Rewrite
8
+
9
+ class RewriteParametersAsThunkCalls
10
+
11
+ def sexp(exp)
12
+ if exp.kind_of? Array
13
+ s(*exp.map { |e| sexp(e) })
14
+ else
15
+ exp
16
+ end
17
+ end
18
+
19
+ def process(sexp)
20
+ raise "Expected a proc" unless sexp[0] == :proc
21
+ raise "Expected a proc with arity >= 1" if sexp[1] == nil
22
+ arguments = sexp[1]
23
+ variable_symbols = if arguments[0] == :dasgn || arguments[0] == :dasgn_curr
24
+ [arguments[1]]
25
+ elsif arguments[0] == :masgn
26
+ arguments[1][1..-1].map { |pair| pair[1] }
27
+ else
28
+ raise "don't know how to extract paramater names from #{arguments}"
29
+ end
30
+
31
+ s(:iter,
32
+ s(:fcall, :lambda),
33
+ sexp(arguments),
34
+ variable_symbols.inject(sexp[2]) { |result, variable|
35
+ VariableRewriter.new(variable, s(:call, s(:dvar, variable), :call)).process(eval(result.inspect))
36
+ }
37
+ )
38
+
39
+ end
40
+
41
+ end
42
+
43
+ # Initialize with a list of names, e.g.
44
+ # CallByThunk.new(:foo, :bar)
45
+ #
46
+ # It then converts expressions of the form foo(expr1, expr2, ..., exprn) into
47
+ # foo.call( lambda { expr1 }, lambda { expr2 }, ..., lambda { exprn })
48
+ #
49
+ # This is handy when combined with RewriteVariablesAsThunkCalls in the following
50
+ # manner: if you rewrite function invocations with CallByThunk and also rewrite the
51
+ # function's body to convert variable references into thunk calls, you now have a
52
+ # function with call-by-name semantics
53
+ #
54
+ class CallByThunk < SexpProcessor
55
+
56
+ def initialize(*functions_to_thunkify)
57
+ @functions_to_thunkify = functions_to_thunkify
58
+ super()
59
+ end
60
+
61
+ def process_fcall(exp)
62
+ qua = exp.dup
63
+ exp.shift
64
+ name = exp.shift
65
+ if @functions_to_thunkify.include? name
66
+ thunked = s(:call, s(:dvar, name), :call)
67
+ unless exp.empty?
68
+ arguments = exp.shift
69
+ raise "Do not understand arguments #{arguments}" unless arguments[0] == :array
70
+ arguments.shift
71
+ thunked_arguments = s(:array)
72
+ until arguments.empty?
73
+ thunked_arguments << s(:iter, s(:fcall, :lambda), nil, process(arguments.shift))
74
+ end
75
+ thunked << thunked_arguments
76
+ end
77
+ else
78
+ thunked = s(:fcall, name) # :fcall, name_of_the_function
79
+ thunked << process(exp.shift) unless exp.empty?
80
+ end
81
+ thunked
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -0,0 +1,191 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'rubygems'
4
+ require 'parse_tree'
5
+ require 'sexp_processor'
6
+ require 'sexp'
7
+
8
+ module Rewrite
9
+
10
+ module Prelude
11
+
12
+ # Adds a guarded method invocation to Ruby:
13
+ #
14
+ # with(andand) do
15
+ # ...
16
+ # @phone = Location.find(:first, ...elided... ).andand.phone
17
+ # ...
18
+ # end
19
+ #
20
+ # It also works with parameters, blocks, and procs passed as blocks.
21
+ #
22
+ # Works by rewriting expressions like:
23
+ #
24
+ # numbers.andand.inject(&:+)
25
+ #
26
+ # Into:
27
+ #
28
+ # lambda { |__1234567890__|
29
+ # if __1234567890__.nil?
30
+ # nil
31
+ # else
32
+ # __1234567890__.inject(&:+)
33
+ # end
34
+ # }.call(numbers)
35
+ #
36
+ class Andand < SexpProcessor
37
+
38
+ def process_iter(exp)
39
+ exp.shift
40
+ receiver_sexp = exp.first #[:call, [:call, [:lit, 1..10], :andand], :inject]
41
+ if receiver_sexp[0] == :call && matches_andand_invocation(receiver_sexp[1])
42
+ exp.shift
43
+ mono_parameter = Rewrite.gensym()
44
+ s(:call,
45
+ s(:iter,
46
+ s(:fcall, :lambda),
47
+ s(:dasgn_curr, mono_parameter),
48
+ s(:if,
49
+ s(:call, s(:dvar, mono_parameter), :nil?),
50
+ s(:nil),
51
+ begin
52
+ s(:iter,
53
+ s(:call,
54
+ s(:dvar, mono_parameter),
55
+ *(receiver_sexp[2..-1].map { |inner| process_inner_expr inner })
56
+ ),
57
+ *(exp.map { |inner| process_inner_expr inner })
58
+ )
59
+ ensure
60
+ exp.clear
61
+ end
62
+ )
63
+ ),
64
+ :call,
65
+ s(:array,
66
+ process_inner_expr(receiver_sexp[1][1]) # s(:lit, 1..10)
67
+ )
68
+ )
69
+ else
70
+ begin
71
+ s(:iter,
72
+ *(exp.map { |inner| process_inner_expr inner })
73
+ )
74
+ ensure
75
+ exp.clear
76
+ end
77
+ end
78
+ end
79
+
80
+ def process_call(exp)
81
+ # s(:call, s(:call, s(:lit, :foo), :andand), :bar)
82
+ exp.shift
83
+ # s(s(:call, s(:lit, :foo), :andand), :bar)
84
+ receiver_sexp = exp.first
85
+ if matches_andand_invocation(receiver_sexp) # s(:call, s(:lit, :foo), :andand)
86
+ exp.shift
87
+ # s( :bar )
88
+ mono_parameter = Rewrite.gensym()
89
+ s(:call,
90
+ s(:iter,
91
+ s(:fcall, :lambda),
92
+ s(:dasgn_curr, mono_parameter),
93
+ s(:if,
94
+ s(:call, s(:dvar, mono_parameter), :nil?),
95
+ s(:nil),
96
+ begin
97
+ s(:call,
98
+ s(:dvar, mono_parameter),
99
+ *(exp.map { |inner| process_inner_expr inner })
100
+ )
101
+ ensure
102
+ exp.clear
103
+ end
104
+ )
105
+ ),
106
+ :call,
107
+ s(:array,
108
+ process_inner_expr(receiver_sexp[1]) # s(:lit, :foo)
109
+ )
110
+ )
111
+ else
112
+ # pass through
113
+ begin
114
+ s(:call,
115
+ *(exp.map { |inner| process_inner_expr inner })
116
+ )
117
+ ensure
118
+ exp.clear
119
+ end
120
+ end
121
+ end
122
+
123
+ def process_block_pass(exp)
124
+ orig = lambda { |exp_dup|
125
+ lambda { Ruby2Ruby.new.process(exp_dup) }
126
+ }.call(exp)
127
+ # [:block_pass, [:lit, :+], [:call, [:call, [:vcall, :foo], :andand], :bar]]
128
+ # [:block_pass, [:lit, :blitz], [:call, [:call, [:vcall, :foo], :andand], :bar, [:array, [:lit, :bash]]]]
129
+ exp.shift
130
+ # [[:lit, :+], [:call, [:call, [:vcall, :foo], :andand], :bar]]
131
+ # [[:lit, :blitz], [:call, [:call, [:vcall, :foo], :andand], :bar, [:array, [:lit, :bash]]]]
132
+ block_exp = process_inner_expr(exp.shift)
133
+ call_exp = exp.shift
134
+ raise "expected block_pass to have a receiver and a call form near #{orig.call}" unless exp.empty?
135
+ # [:call, [:call, [:vcall, :foo], :andand], :bar]
136
+ # [:call, [:call, [:vcall, :foo], :andand], :bar, [:array, [:lit, :bash]]]
137
+ raise 'confused' unless call_exp.shift == :call
138
+ # [[:call, [:vcall, :foo], :andand], :bar]
139
+ # [[:call, [:vcall, :foo], :andand], :bar, [:array, [:lit, :bash]]]
140
+ receiver_sexp = call_exp.first
141
+ if matches_andand_invocation(receiver_sexp) # [:call, [:vcall, :foo], :andand]
142
+ call_exp.shift
143
+ # [:bar]
144
+ # [:bar, [:array, [:lit, :bash]]]
145
+ mono_parameter = Rewrite.gensym()
146
+ s(:call,
147
+ s(:iter,
148
+ s(:fcall, :lambda),
149
+ s(:dasgn_curr, mono_parameter),
150
+ s(:if,
151
+ s(:call, s(:dvar, mono_parameter), :nil?),
152
+ s(:nil),
153
+ s(:block_pass,
154
+ block_exp,
155
+ s(:call,
156
+ s(:dvar, mono_parameter),
157
+ *(call_exp.map { |inner| process_inner_expr inner })
158
+ )
159
+ )
160
+ )
161
+ ),
162
+ :call,
163
+ s(:array,
164
+ process_inner_expr(receiver_sexp[1]) # s(:lit, :foo)
165
+ )
166
+ )
167
+ else
168
+ s(:block_pass,
169
+ block_exp,
170
+ s(:call,
171
+ *(call_exp.map { |inner| process_inner_expr inner })
172
+ )
173
+ )
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def process_inner_expr(inner)
180
+ inner.kind_of?(Array) ? process(inner) : inner
181
+ end
182
+
183
+ def matches_andand_invocation(sexp)
184
+ sexp[0] == :call && sexp[2] == :andand
185
+ end
186
+
187
+ end
188
+
189
+ end
190
+
191
+ end
@@ -0,0 +1,94 @@
1
+ module Rewrite
2
+
3
+ module Prelude
4
+
5
+ # Warning: Extreme Funk ahead:
6
+ #
7
+ # CalledByName allows you to define your own function-like-things that are called by name rather than
8
+ # by value. Here is a trivial example:
9
+ #
10
+ # with(
11
+ # called_by_name(:my_or) { |arg1, arg2|
12
+ # if arg1
13
+ # true
14
+ # else
15
+ # !!arg2
16
+ # end
17
+ # }
18
+ # ) do
19
+ # ...
20
+ # my_or(
21
+ # true, nil.raise_missing_method_exception
22
+ # )
23
+ # ...
24
+ # end
25
+ #
26
+ # First, and most obviously, you get something that looks like a real function. Procs in Ruby are just objects
27
+ # that capture the bindings where theire initializing block was created, and you have to invoke them using #call
28
+ # or #[].
29
+ #
30
+ # Second, and this is the funky bit, parameters inside of your function-like-thing are called by name. Meaning,
31
+ # when you call your function-like-thing, instead of evaluating each parameter's expression and passing its
32
+ # value, Ruby passes the expression itself. The expression is not evaluated until the parpameter is actually used,
33
+ # and if you use it more than once the expression is evaluated more than once. This matters when there are side
34
+ # effects.
35
+ #
36
+ # The example above shows how call-by-name can be used to implement short-circuit evaluation, something that is
37
+ # not possible in Ruby without a lot of boilerplate introducing lambdas. When you call my_or, it evaluates arg1
38
+ # as part of the if statement, that evaluates to true, and it never evaluates arg2. Therefore, it never tries
39
+ # to send a method to nil and thus never raises an exception.
40
+ #
41
+ # Works by rewriting:
42
+ #
43
+ # with(
44
+ # called_by_name(:my_or) { |arg1, arg2|
45
+ # if arg1
46
+ # true
47
+ # else
48
+ # !!arg2
49
+ # end
50
+ # }
51
+ # ) do
52
+ # ...
53
+ # my_or(
54
+ # true, nil.raise_missing_method_exception
55
+ # )
56
+ # ...
57
+ # end
58
+ #
59
+ # Into:
60
+ #
61
+ # lambda { |my_or|
62
+ # ...
63
+ # my_or.call(
64
+ # lambda { true }, lambda { nil.raise_missing_method_exception }
65
+ # )
66
+ # ...
67
+ # }.call(
68
+ # lambda { |arg1, arg2|
69
+ # if arg1.call
70
+ # true
71
+ # else
72
+ # !!arg2.call
73
+ # end
74
+ # }
75
+ # )
76
+ #
77
+ class CalledByName
78
+
79
+ def initialize(name, &proc)
80
+ @let = Rewrite::DefVar.new(name, Rewrite::RewriteParametersAsThunkCalls.new.process(eval(proc.to_sexp.inspect)))
81
+ @call_by_thunk = Rewrite::CallByThunk.new(name)
82
+ end
83
+
84
+ def process(exp)
85
+ @let.process(
86
+ @call_by_thunk.process(exp)
87
+ )
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
@@ -0,0 +1,43 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ Dir["#{File.dirname(__FILE__)}/prelude/*.rb"].each do |element|
5
+ require element
6
+ end
7
+
8
+
9
+ module Rewrite
10
+
11
+ # Module containing standard rewriting features. For more information about each feature, go directly to the class,
12
+ # such as Andand or CalledByName. If you <tt>include Rewrite::Prelude</tt>, you get some methods as sugar. For a
13
+ # list of those methods, go to InstanceMethods.
14
+ #--
15
+ # TODO: Including the prelude should mean that all of these classes are processed by default, I shouldn't
16
+ # have to write them all out as instance methods
17
+ module Prelude
18
+
19
+ module ClassMethods #:nodoc: all
20
+
21
+ end
22
+
23
+ module InstanceMethods
24
+
25
+ # Create a new CalledByName rewriter
26
+ def called_by_name(name, &proc)
27
+ CalledByName.new(name, &proc)
28
+ end
29
+
30
+ # Instantiate Andand rewriting
31
+ def andand()
32
+ Andand.new
33
+ end
34
+
35
+ end
36
+ def self.included(receiver) #:nodoc: all
37
+ receiver.extend ClassMethods
38
+ receiver.send :include, InstanceMethods
39
+ end
40
+
41
+ end
42
+
43
+ end