Here's another go at it using continuations:
% cat try_catch_throw.rb
class Flow
def initialize(parent = nil)
@exceptions = []
@parent = parent
@uncaught_exception = nil
end
def try(&block)
@try_block = block
end
def throw(exception)
caught = @exceptions.each do |key, value|
if key === exception
value.call(exception)
break true
end
end
unless caught == true
if @parent
@parent.throw(exception)
else
@uncaught_exception = exception
end
end
@cc.call
end
def finally(&block)
@finally = block
self
end
def catch(exception, &block)
@exceptions << [exception, block]
self
end
def go
callcc { |@cc| @try_block.call }
if @finally
@finally.call
end
if @uncaught_exception
STDERR.puts "Uncaught exception: #{@uncaught_exception}"
exit(1)
end
end
end
% cat test_flow.rb
require 'try_catch_throw'
handling2 = Flow.new
handling2.try {
handling3 = Flow.new(handling2)
handling3.try {
puts "Nested try"
handling3.throw "ERROR! in handling3"
}
handling3.go
}
handling2.catch(String) do |exception|
puts "handling2 Caught exception: #{exception}"
end
handling2.go
handling = Flow.new
handling.try {
puts "Hello"
handling.throw "ERROR!"
puts "World"
}
handling.finally {
puts "Finally"
}
handling.go
% ruby test_flow.rb
Nested try
handling2 Caught exception: ERROR! in handling3
Hello
Finally
Uncaught exception: ERROR!
At first I tried to do it without continuations but I couldn't think
of a way to do so. Also the nesting is explicit which has the
advantage of not using global vars and the disadvantage of excessive
typing.