Hi all,
Here's my solution which uses Ruby only and modifies core classes and
restores them at the end of the function. I wouldn't trust it ;).
Maybe this would be a task for Why's Sandbox library? Create a sandbox,
modify the core classes there and evaluate the blocks there. It's just
an idea, I don't know if it's possible.
Robin Stocker
class SxpGenerator
def method_missing(meth, *args)
[meth, *args]
end
BINARY_METHODS = [:+, :-, :*, :/, :%, :**, :^, :<, :>, :<=, :>=, :==]
def self.overwrite_methods(mod)
BINARY_METHODS.each do |method|
mod.module_eval do
if method_defined? method
alias_method "__orig_#{method}__", method
end
define_method method do |arg|
[method, self, arg]
end
end
end
end
def self.restore_methods(mod)
BINARY_METHODS.each do |method|
mod.module_eval do
orig_method = "__orig_#{method}__"
if method_defined? orig_method
alias_method method, orig_method
remove_method orig_method
else
remove_method method
end
end
end
end
end
def sxp(&block)
klasses = [Fixnum, Bignum, Symbol, Array, Float, String]
klasses.each do |klass|
SxpGenerator.overwrite_methods(klass)
end
begin
result = SxpGenerator.new.instance_eval &block
rescue Exception
result = nil
end
klasses.each do |klass|
SxpGenerator.restore_methods(klass)
end
result
end
require 'test/unit'
class TestSxp < Test::Unit::TestCase
def test_function
assert_equal [:max, [:count, :name]], sxp { max(count(:name)) }
end
def test_number
assert_equal 8, sxp { 8 }
assert_equal [:+, 3, 4], sxp { 3 + 4 }
assert_equal [:+, 3, :symbol], sxp { 3 + :symbol }
assert_equal [:/, 7, :field], sxp { 7 / :field }
end
def test_symbol
assert_equal [:>, :field, 5], sxp { :field > 5 }
assert_equal [:==, :field1, :field2], sxp { :field1 == :field2 }
end
def test_mixed
assert_equal [:count, [:+, 3, 7]], sxp { count(3+7) }
assert_equal [:+, 3, [:count, :field]], sxp { 3 + count(:field) }
end
def test_environment
assert_equal [:-, 10, [:count, [:*, :field, 4]]],
sxp { 10 - count(:field * 4) }
assert_raise(TypeError) { 7 / :field }
assert_raise(NoMethodError) { 7 + count(:field) }
assert_equal 11, 5 + 6
assert_raise(NoMethodError) { :field > 5 }
end
end