Status = Struct.new(:text, :pos, :matched, :named, :group_stack, :data)

def deepcopy(a)
  Marshal.load Marshal.dump a
end
class Object
  unless method_defined?(:tap)
    def tap
      yield self
      self
    end
  end
end

def match_string(str)
  len = str.size
  lambda{|nextpart|
    lambda{|status|
       if status.text[status.pos, len] == str
          nextpart.call(deepcopy(status).tap{|s| 
             s.pos     += len 
             s.matched << str
          })
       end
    }
  }
end

def match_eos
  lambda{|nextpart|
    lambda{|status|
       if status.pos == status.text.size
          nextpart.call(status)
       end
    }
  }
end

def match_charset(str)
  lambda{|nextpart|
    lambda{|status|
       next if status.pos == status.text.size
       if str[status.text[status.pos, 1]]
          nextpart.call(deepcopy(status).tap{|s| 
   	     s.matched << s.text[s.pos, 1]
             s.pos     += 1
          })
       end
    }
  }
end

def match_ok()
 lambda{|nextpart|
  lambda{|status|
     p status
  }
 }
end

class Proc
  def >>(rhs)
    lambda{|nextpart|
      self.call(rhs.call(nextpart))
    }     
  end
  alias & >> 

  def |(rhs)
    lambda{|np|
       lambda{|status|
         self.call(np).call(deepcopy(status))
         rhs.call(np).call(deepcopy(status))
       }
    }
  end

  def *(rep = nil)
    f = lambda{|np|
      lambda{|s|
        (self >> f).call(np).call(deepcopy(s))
        np.call(deepcopy(s))
      }
    }
  end

  def nocapture
    self >> lambda{|np| lambda{|s| s.matched.pop; np.call(s)}} 
  end

  
end

def match_any
  lambda{|np| lambda{|s| yield(np, s) } }
end

def match_nil
  lambda{|np| lambda{|s| np.call(s)}}
end

def group_begin(name)
  lambda{|np| lambda{|s|  
	s.group_stack.push([name, s.matched.length]); 
	np.call(s)} 
  }
end

def group_end
  lambda{|np| lambda{|s|  
     name, start = s.group_stack.pop
     s.named[name] = s.matched[start..-1].join
     np.call(s)
  } }    
end

def eval_expr(str)
  st = Status.new 
  st.pos = 0
  st.matched = []
  st.named = {}
  st.group_stack = []
  st.text = str
  st.data = []
  expr, additive, multitive, num,expr_bracket, single, calc = 'decl'
  
  expr = match_any{|np, st| additive.call(np).call(st) }
  expr_bracket = match_string("(") & expr & match_string(")") & match_any{|np, st|  
      a,b,c=st.matched.slice!(-3..-1);
      st.matched.push calc[b]	
      np.call(st)
  }

  calc = lambda{|b|
     next b unless Array === b
     ret = b[1]
     i = 2
     while i < b.size
	     ret = ret.send(b[i], b[i+1]) rescue p(b)
	i += 2
     end
     ret
  }

  additive = match_any{|np, st|
    (
     multitive | 
     multitive & match_charset("+-") & additive & match_any{|np, st| 
   	     a,b,c = st.matched.slice!(-3..-1); 
	     a = calc[a] if Array === a && a[0] == :mul
	     c = calc[c] if Array === c && c[0] == :mul
	     arr = [:add, a, b, c] 
	     arr = [:add, a, b, *c[1..-1]]      if Array === c && c[0] == :add
             st.matched.push arr
	     np.call(st)
     }
   ).call(np).call(st)
  }


  multitive = match_any{|np, st|
   (
     single | 
     single & match_charset("*/") & multitive & match_any{|np, st| 
	     a,b,c = st.matched.slice!(-3..-1)
	     arr = [:mul, a, b, c] 
	     arr = [:mul, a, b, *c[1..-1]] if Array === c && c[0] == :mul
             st.matched.push arr
	     np.call(st)
     }   
   ).call(np).call(st)
  }
   
  num = match_any{|np, st|
    if a = st.text[st.pos..-1][/^(\d+)/, 1]
      s = deepcopy(st)
      s.matched << a.to_i
      s.pos += a.length
      np.call(s)
    end
  }
  single = expr_bracket | num
  ret = nil
  (expr >> match_eos).call(lambda{|st|ret = st.matched[-1]}).call(st)
  calc[ret]
end

p eval_expr("222*67+192*(37-48)")
