PackratパーサをRubyで コンビネータ版

とりあえず、alternativeの / 演算子と、連接の ** 演算子を実装してみた
ユーザコードをうまく紛れ込ます方法はどうするかいな

PEG らしさのために / を使うと、それより優先順位が高いのは ** しかない件
演算子一覧を見ると ~ があるけど、これは単項演算子
記号列ならなんだって演算子になる言語のほうが、言語内DSL力は高いということか

#!/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 s
      @memo = Hash.new do|d, key|
         case key
         when :additive
            P::Additive.call d
         when :multitive
            P::Multitive.call d
         when :primary
            P::Primary.call d
         when :decimal
            P::Decimal.call d
         when :char
            if s.empty? then
               nil
            else
               [s[0, 1], Derivs.new(s[1..-1])]
            end
         when :input
            s
         else
            raise
         end
      end
   end

   def [] term
      @memo[term]
   end
end

class Parser
   def set a=nil, &c
      if a and c then raise end
      unless a or c then raise end
      @func = a || c
   end

   def call d
      @func.call d
   end

   def ** other
      result = Parser.new
      result.set do|d|
         if r = self.call(d) then
            vleft, dd = r
            if rr = other.call(dd) then
               vright, ddd = rr
               [[vleft, vright], ddd]
            else
               nil
            end
         else
            nil
         end
      end
      result
   end

   def / other
      result = Parser.new
      result.set do|d|
         self.call d or other.call d
      end
      result
   end
end

module ParserUtil
   def char c
      result = Parser.new
      result.set do|d|
         if r = d[:char] then
            cc, dd = r
            if cc == c then
               [cc, dd]
            else
               nil
            end
         else
            nil
         end
      end
      result
   end

   def success v
      result = Parser.new
      result.set do|d|
         [v, d]
      end
      result
   end

   def fail
      result = Parser.new
      result.set do|d|
         nil
      end
      result
   end
end

module P
   extend ParserUtil

   Additive = Parser.new
   Multitive = Parser.new
   Primary = Parser.new
   Decimal = Parser.new

   Additive.set Multitive ** char('+') ** Additive / Multitive

   Multitive.set Primary ** char('*') ** Multitive / Primary

   Primary.set char('(') ** Additive ** char(')') / Decimal

   Decimal.set char('0') / char('1') / char('2') / char('3') / char('4') /
      char('5') / char('6') / char('7') / char('8') / char('9')
end

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