Skip to content

Instantly share code, notes, and snippets.

@hosiawak
Created June 30, 2011 17:50
Show Gist options
  • Save hosiawak/1056766 to your computer and use it in GitHub Desktop.
Save hosiawak/1056766 to your computer and use it in GitHub Desktop.
module Macros
module Andand
ANDAND = :andand
def process_call(line, receiver, name, arguments)
if receiver.kind_of?(Rubinius::AST::Send) && receiver.name == ANDAND
local_var = gensym
assgn = Rubinius::AST::LocalVariableAssignment.new(line, local_var, receiver.receiver)
left = Rubinius::AST::LocalVariableAccess.new(line, local_var)
if arguments
# SendWithArguments
right = Rubinius::AST::SendWithArguments.new(line, left, name, arguments)
else
# Send
right = Rubinius::AST::Send.new(line, left, name)
end
and_node = Rubinius::AST::And.new(line, left, right)
Rubinius::AST::Block.new(line, [assgn, and_node])
else
super
end
end
def process_iter(line, method_send, arguments, body)
if method_send.kind_of?(Rubinius::AST::Block)
# [1,2,3].andand.inject {|sum,n| sum + n}
method_send.array.last.right.block = Rubinius::AST::Iter.new(line, arguments, body)
method_send
elsif method_send.name == ANDAND
if arguments
# 'foo'.andand {|fu| fu }
# fu = 'foo' and fu
assgn = Rubinius::AST::LocalVariableAssignment.new(line, arguments.name, method_send.receiver)
Rubinius::AST::And.new(line, assgn, body)
else
# 'foo'.andand { :bar }
# 'foo' and :bar
Rubinius::AST::And.new(line, method_send.receiver, body)
end
else
super
end
end
def process_block_pass(line, method_send, body)
if method_send.kind_of?(Rubinius::AST::Block)
# [1,2].andand.map(&:to_s)
body = Rubinius::AST::LocalVariableAccess.new(line, body.name)
method_send.array.last.right.block = Rubinius::AST::BlockPass.new(line, body)
method_send
elsif method_send.name == ANDAND
# :foo.andand(&blk)
# :foo and blk.call
right = Rubinius::AST::Send.new(line, body, :call)
Rubinius::AST::And.new(line, method_send.receiver, right)
else
super
end
end
end
end
describe "Andand macro" do
it "returns the left hand argument if the right is missing" do
:foo.andand.should == :foo
end
it "rewrites foo.andand.bar into __x__ = foo; x && x.bar" do
[1,2,3].andand.to_s.should == "123"
nil.andand.to_s.should == nil
end
it "rewrites sends with 1 argument" do
[1,2,3].andand.inject(:+).should == 6
#
end
it "rewrites sends with many arguments" do
[1,2,3].andand.inject(0, :+).should == 6
end
it "rewrites sends with block" do
[1,2,3].andand.inject { |sum,n| sum + n}.should == 6
end
it "rewrites sends with passed block" do
sum = proc { |sum,n| sum + n}
[1,2,3].andand.inject(&sum).should == 6
end
it "rewrites sends with passed block and argument" do
sum = proc { |sum,n| sum + n}
[1,2,3].andand.inject(0, &sum).should == 6
end
it "rewrites sends with block and argument" do
[1,2,3].andand.inject(0) { |sum,n| sum + n}.should == 6
end
describe "with a block on the right side" do
it "passes left argument as a block parameter" do
'foo'.andand { |fu| fu + 'bar'}.should == 'foobar'
# fu = 'foo' and fu + 'bar'
#
end
it "degenerates block with no parameter and one statement" do
# 'foo' and :bar
'foo'.andand { :bar }.should == :bar
end
it "accepts a passed block" do
b = proc { :bar }
'foo'.andand(&b).should == :bar
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment