#!/usr/bin/env ruby # (c) 12/2006 Yoann Guillot # this code is licenced under the terms of the WTFPL # full text available at http://sam.zoy.org/wtfpl/ # a simple math expression parser & evaluator # does not handle unary operators or not fully parenthesised expressions require 'strscan' class Node def initialize(l, op, r) @l, @op, @r = l, op, r end def eval(values) l = if @l.kind_of? Node @l.eval(values) else values[@l] end r = if @r.kind_of? Node @r.eval(values) else values[@r] end l.send(@op, r) end def self.gettok(ss) ss.scan(/\s*/) if ss.scan(/\(/) e = getexpr(ss) raise 'missing )' unless ss.scan(/\)/) e else ss.scan(/\w+/) end end def self.getexpr(ss) l = gettok(ss) ss.scan(/\s*/) # put all operators here return l if not op = ss.scan(/[-+&|]/) r = gettok(ss) new(l, op, r) end def self.calc(str, values) e = getexpr(StringScanner.new(str)) if e.kind_of? self e.eval(values) else values[e] end end end values = { 'foo' => [1, 2, 3], 'bar' => [3, 4, 5], 'baz' => [4] } p Node.calc('(foo | bar) - baz', values)