とりあえずRubyのステート・マシンを作ってみた

うーん、果たして他の言語に応用できるもんか…

%%{
  machine ruby_machine;
  action Y { yield(data, head, fpc); head = fpc + 1; }
  main := ( (lower+ ' ') @Y )* ;
}%%

class RubyMachine
  %% write data;

  def self.parse(data)
    head = 0
    %% write init;
    %% write exec;
  end
end

RubyMachine.parse("abcd efg hijk ") do |data, head, fpc|
  p data.slice(head..fpc)
end

value分かりずらい。まんま部分文字列取れたらいいのに。

出力はこんな感じ。


"abcd "
"efg "
"hijk "

Rubyファイルがこんな感じ。まあ、読めないです。

# line 1 "ruby_machine.rl"
# line 5 "ruby_machine.rl"


class RubyMachine
  
# line 8 "ruby_machine.rb"
class << self
	attr_accessor :_ruby_machine_actions
	private :_ruby_machine_actions, :_ruby_machine_actions=
end
self._ruby_machine_actions = [
	0, 1, 0
]

class << self
	attr_accessor :_ruby_machine_key_offsets
	private :_ruby_machine_key_offsets, :_ruby_machine_key_offsets=
end
self._ruby_machine_key_offsets = [
	0, 0, 3
]

class << self
	attr_accessor :_ruby_machine_trans_keys
	private :_ruby_machine_trans_keys, :_ruby_machine_trans_keys=
end
self._ruby_machine_trans_keys = [
	32, 97, 122, 97, 122, 0
]

class << self
	attr_accessor :_ruby_machine_single_lengths
	private :_ruby_machine_single_lengths, :_ruby_machine_single_lengths=
end
self._ruby_machine_single_lengths = [
	0, 1, 0
]

class << self
	attr_accessor :_ruby_machine_range_lengths
	private :_ruby_machine_range_lengths, :_ruby_machine_range_lengths=
end
self._ruby_machine_range_lengths = [
	0, 1, 1
]

class << self
	attr_accessor :_ruby_machine_index_offsets
	private :_ruby_machine_index_offsets, :_ruby_machine_index_offsets=
end
self._ruby_machine_index_offsets = [
	0, 0, 3
]

class << self
	attr_accessor :_ruby_machine_trans_targs_wi
	private :_ruby_machine_trans_targs_wi, :_ruby_machine_trans_targs_wi=
end
self._ruby_machine_trans_targs_wi = [
	2, 1, 0, 1, 0, 0
]

class << self
	attr_accessor :_ruby_machine_trans_actions_wi
	private :_ruby_machine_trans_actions_wi, :_ruby_machine_trans_actions_wi=
end
self._ruby_machine_trans_actions_wi = [
	1, 0, 0, 0, 0, 0
]

class << self
	attr_accessor :ruby_machine_start
end
self.ruby_machine_start = 2;
class << self
	attr_accessor :ruby_machine_first_final
end
self.ruby_machine_first_final = 2;
class << self
	attr_accessor :ruby_machine_error
end
self.ruby_machine_error = 0;

class << self
	attr_accessor :ruby_machine_en_main
end
self.ruby_machine_en_main = 2;

# line 9 "ruby_machine.rl"

  def self.parse(data)
    head = 0
    
# line 96 "ruby_machine.rb"
begin
	p ||= 0
	pe ||= data.length
	cs = ruby_machine_start
end
# line 13 "ruby_machine.rl"
    
# line 104 "ruby_machine.rb"
begin
	_klen, _trans, _keys, _acts, _nacts = nil
	_goto_level = 0
	_resume = 10
	_eof_trans = 15
	_again = 20
	_test_eof = 30
	_out = 40
	while true
	_trigger_goto = false
	if _goto_level <= 0
	if p == pe
		_goto_level = _test_eof
		next
	end
	if cs == 0
		_goto_level = _out
		next
	end
	end
	if _goto_level <= _resume
	_keys = _ruby_machine_key_offsets[cs]
	_trans = _ruby_machine_index_offsets[cs]
	_klen = _ruby_machine_single_lengths[cs]
	_break_match = false
	
	begin
	  if _klen > 0
	     _lower = _keys
	     _upper = _keys + _klen - 1

	     loop do
	        break if _upper < _lower
	        _mid = _lower + ( (_upper - _lower) >> 1 )

	        if data[p] < _ruby_machine_trans_keys[_mid]
	           _upper = _mid - 1
	        elsif data[p] > _ruby_machine_trans_keys[_mid]
	           _lower = _mid + 1
	        else
	           _trans += (_mid - _keys)
	           _break_match = true
	           break
	        end
	     end # loop
	     break if _break_match
	     _keys += _klen
	     _trans += _klen
	  end
	  _klen = _ruby_machine_range_lengths[cs]
	  if _klen > 0
	     _lower = _keys
	     _upper = _keys + (_klen << 1) - 2
	     loop do
	        break if _upper < _lower
	        _mid = _lower + (((_upper-_lower) >> 1) & ~1)
	        if data[p] < _ruby_machine_trans_keys[_mid]
	          _upper = _mid - 2
	        elsif data[p] > _ruby_machine_trans_keys[_mid+1]
	          _lower = _mid + 2
	        else
	          _trans += ((_mid - _keys) >> 1)
	          _break_match = true
	          break
	        end
	     end # loop
	     break if _break_match
	     _trans += _klen
	  end
	end while false
	cs = _ruby_machine_trans_targs_wi[_trans]
	if _ruby_machine_trans_actions_wi[_trans] != 0
		_acts = _ruby_machine_trans_actions_wi[_trans]
		_nacts = _ruby_machine_actions[_acts]
		_acts += 1
		while _nacts > 0
			_nacts -= 1
			_acts += 1
			case _ruby_machine_actions[_acts - 1]
when 0 then
# line 3 "ruby_machine.rl"
		begin
 yield(data, head, p); head = p + 1; 		end
# line 3 "ruby_machine.rl"
# line 189 "ruby_machine.rb"
			end # action switch
		end
	end
	if _trigger_goto
		next
	end
	end
	if _goto_level <= _again
	if cs == 0
		_goto_level = _out
		next
	end
	p += 1
	if p != pe
		_goto_level = _resume
		next
	end
	end
	if _goto_level <= _test_eof
	end
	if _goto_level <= _out
		break
	end
	end
	end
# line 14 "ruby_machine.rl"
  end
end

RubyMachine.parse("abcd efg hijk ") do |data, head, fpc|
  p data.slice(head..fpc)
end