9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

遠い世界の数式 (RubyVM::InstructionSequence.load)

Last updated at Posted at 2015-01-18

問題:http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/

解法として、まじめに構文解析するか、正規表現を使うか( http://qiita.com/cielavenir/items/ad6338a8cc4fa9c99704 )、Rubyのevalを何とかして使うかということになると思う。

Rubyのevalを利用したい場合、(鍋谷さんの解法のように数値を全て別クラスに分岐することをしなければ)このように、どうしてもモンキーパッチが必要になる (Refinementで可能かどうかの確認はしていない)

20230523: Refinementで書き直しました.

kanagawarb_evalex_eval.rb
#!/usr/bin/ruby
#http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/

Fixnum=Integer if !defined?(Fixnum)
module FixnumHack
	refine Fixnum do
		alias_method :or_real,:|
		alias_method :and_real,:&
		alias_method :plus_real,:+
		alias_method :mult_real,:*
		def |(other) self.mult_real(other) end
		def &(other) self.plus_real(other) end
		def +(other) self.and_real(other) end
		def *(other) self.or_real(other) end
	end
end
class Impl
	using FixnumHack
	def solve(s)
		eval(s.tr('*+&|','|&+*'))
	end
end

while gets
	p Impl.new.solve $_
end

既存メソッドの上書きを何とか回避できないものかと考えるうちに、「コンパイルされたバイトコードを上書きする」方法を考えついた。そのためには、librubyのrb_iseq_loadをRubyの世界から見えるようにすれば良い。

kanagawarb_evalex_iseq.rb
#!/usr/bin/ruby
#http://nabetani.sakura.ne.jp/kanagawa.rb/evalex/

begin
	require 'iseq'
rescue LoadError
	require 'fiddle'

	class RubyVM
		class InstructionSequence
			addr = Fiddle.dlopen(nil)['rb_iseq_load']
			fn   = Fiddle::Function.new(addr, [Fiddle::TYPE_VOIDP] * 3, Fiddle::TYPE_VOIDP)
			define_singleton_method(:load) do |dat, par=nil, opt=nil|
				fn.call(Fiddle.dlwrap(dat), par, opt).to_value
			end
		end
	end
end

while gets
	bytecode=RubyVM::InstructionSequence.compile($_.tr('*+&|','|&+*')).to_a
	bytecode[13].map!{|e|
		if e[1].is_a?(Hash)&&e[1].has_key?(:mid)
			e[1][:mid]=e[1][:mid].to_s.tr('*+&|','|&+*').to_sym
			e[0]={:* => :opt_mult,:+ => :opt_plus,:& => :opt_send_simple,:| => :opt_send_simple}[e[1][:mid]]
		end
		e
	}
	p RubyVM::InstructionSequence.load(bytecode).eval
end

##最後に
わかっているとは思いますが、RubyVM::InstructionSequence.load解法はネタです(これ以上邪悪な方法はないと思います)。実務ではやらないようにして下さい^^;;

9
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?