def process_defn(exp)
name = exp.shift
args = s(:args)
body = process exp.shift
case body.first
when :scope, :fbody then
body = body[1] if body.first == :fbody
args = body.last[1]
assert_type args, :args
assert_type body, :scope
assert_type body[1], :block
body.last.delete_at 1
when :bmethod then
body.shift
dasgn = body.shift
assert_type dasgn, :dasgn_curr
dasgn.shift
args.push(*dasgn)
body.find_and_replace_all(:dvar, :lvar)
if body.first.first == :block then
body = s(:scope, body.shift)
else
body = s(:scope, s(:block, body.shift))
end
when :dmethod
body = body[2][1][2]
args = body[1]
body.delete_at 1
body = s(:scope, body)
when :ivar then
body = s(:scope, s(:block, s(:return, body)))
when :attrset then
argname = body.last
args << :arg
body = s(:scope, s(:block, s(:return, s(:iasgn, argname, s(:lvar, :arg)))))
else
raise "Unknown :defn format: #{name.inspect} #{args.inspect} #{body.inspect}"
end
if Array === args.last and args.last.first == :block then
cond = args.pop
cond.shift
new_code = cond.map do |t, var, val|
s(:if, s(:call, s(:lvar, var), :nil?), s(:lasgn, var, val), nil)
end
body[1].insert 1, *new_code
end
return s(:defn, name, args, body)
end