Packrat パーサを手書き by Ruby

とりあえずこんなのでいいのかな
パーサコンビネータは(作って/使って)ない
(あとで気づいたけど、ダメだこれ。tailのメモ化ができていない。というか、いちいちクロージャにしなくてもよかった)

#!/usr/local/bin/ruby19 -w
# coding:utf-8
# vi:set ts=3 sw=3:
# vim:set sts=0 noet:

require 'pp'

class Derivs
   def initialize str
      @memo = Hash.new do|hash, key|
         case key
         when :additive
            pAdditive hash
         when :multitive
            pMultitive hash
         when :primary
            pPrimary hash
         when :decimal
            pDecimal hash
         when :char
            if str.empty? then
               nil
            else
               [str[0, 1], proc{Derivs.new str[1..-1]}]
            end
         else
            raise
         end
      end
   end

   def [] term
      @memo[term]
   end
end

def pAdditive d
   if r1 = d[:multitive] then
      vleft, d1_ = r1
      if r2 = d1_.call[:char] then
         c, d2_ = r2
         if c == '+' then
            if r3 = d2_.call[:additive] then
               vright, d3_ = r3
               return [vleft + vright, d3_]
            end
         end
      end
   end

   if r = d[:multitive] then
      r
   else
      nil
   end
end

def pMultitive d
   if r1 = d[:primary] then
      vleft, d1_ = r1
      if r2 = d1_.call[:char] then
         c, d2_ = r2
         if c == '*' then
            if r3 = d2_.call[:multitive] then
               vright, d3_ = r3
               return [vleft * vright, d3_]
            end
         end
      end
   end

   if r = d[:primary] then
      r
   else
      nil
   end
end

def pPrimary d
   if r1 = d[:char] then
      c, d1_ = r1
      if c == '(' then
         if r2 = d1_.call[:additive] then
            v, d2_ = r2
            if r3 = d2_.call[:char] then
               c, d3_ = r3
               if c == ')' then
                  return [v, d3_]
               end
            end
         end
      end
   end

   if r = d[:decimal] then
      r
   else
      nil
   end
end

def pDecimal d
   if r = d[:char] then
      c, d1_ = r
      case c
      when '0' then
         [0, d1_]
      when '1' then
         [1, d1_]
      when '2' then
         [2, d1_]
      when '3' then
         [3, d1_]
      when '4' then
         [4, d1_]
      when '5' then
         [5, d1_]
      when '6' then
         [6, d1_]
      when '7' then
         [7, d1_]
      when '8' then
         [8, d1_]
      when '9' then
         [9, d1_]
      else
         nil
      end
   else
      nil
   end
end

d = Derivs.new '(2+3)*(4+5)'
pp d[:additive]