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]