Saturday, May 15, 2010

Interpreter

tc_ast.rb
require "test/unit"

require_relative "ast"

class TestAst < Test::Unit::TestCase
  def test_bigger
    assert_equal((91..100).to_a, bigger(90).evaluate(1..100))
  end
 
  def test_smaller
    assert_equal([1,4,9,12], smaller(15).evaluate([1,20,4,66,23,9,40,12]))
  end

  def test_even
    assert_equal([2,4,6,8,10,12,14,16], even.evaluate(1..16))
  end
 
  def test_odd
    assert_equal([1,3,5,7,9,11,13,15], odd.evaluate(1..16))
  end
 
  def test_bigger_and_even
    assert_equal([32,44,56],
                 ( bigger(30) & even ).evaluate([4,32,11,23,56,7,43,44,2,14]))
  end
 
  def test_smaller_and_odd
    assert_equal([7,11,23],
                 ( smaller(30) & odd ).evaluate([4,32,11,23,56,7,43,44,2,14]))
  end
 
  def test_bigger_or_even
    assert_equal([2,4,14,32,43,44,56],
                 ( bigger(30) | even ).evaluate([4,32,11,23,56,7,43,44,2,14]))
  end
 
  def test_smaller_or_odd
    assert_equal([2,4,7,11,14,23,43],
                 ( smaller(30) | odd ).evaluate([4,32,11,23,56,7,43,44,2,14]))
  end

  def test_bigger_and_even_or_smaller_and_odd
    assert_equal([7,11,23,44,56],
                 ( ( bigger(40) & even ) | ( smaller(30) & odd ))
                 .evaluate([4,32,11,23,56,7,43,44,2,14]))
  end
end

ast.rb
#!/usr/local/bin/ruby
# -*- encoding:utf-8 -*-

class Expression
  def evaluate(context)
    context.select { |x| yield(x) }
  end
 
  def &(other)
    And.new(self, other)
  end
 
  def |(other)
    Or.new(self, other)
  end
end

class Bigger < Expression
  def initialize(num)
    @num = num
  end
 
  def evaluate(context)
    super { |n| n > @num }
  end
end

class Smaller < Expression
  def initialize(num)
    @num = num
  end
 
  def evaluate(context)
    super { |n| n < @num }
  end
end

class Even < Expression
  def evaluate(context)
    super { |n| n.even? }
  end
end

class Odd < Expression
  def evaluate(context)
    super { |n| n.odd? }
  end
end

class And < Expression
  def initialize(exp1, exp2)
    @exp1, @exp2 = exp1, exp2
  end
 
  def evaluate(context)
    (@exp1.evaluate(context) & @exp2.evaluate(context)).sort
  end
end

class Or < Expression
  def initialize(exp1, exp2)
    @exp1, @exp2 = exp1, exp2
  end
 
  def evaluate(context)
    (@exp1.evaluate(context) | @exp2.evaluate(context)).sort
  end
end

def bigger(num)
  Bigger.new(num)
end

def smaller(num)
  Smaller.new(num)
end

def even
  Even.new
end

def odd
  Odd.new
end

interpreter = bigger(70) & even | smaller(20) & odd
p interpreter.evaluate(1..100)

def extract(context)
  (context.select { |n| n > 70 }.select { |n| n.even? } |
   context.select { |n| n < 20 }.select { |n| n.odd? }).sort
end
p extract(1..100)


No comments: