rewrite 0.0.1 → 0.0.2

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