Experiments with ruby-processing (processing-2.2.1) and JRubyArt for processing-3.0

Showing posts with label behaviour driven development. Show all posts
Showing posts with label behaviour driven development. Show all posts

Monday, 28 January 2013

Behaviour driven development rspec

Here is an initial specification for a context sensitive LSystem in ruby (example taken from abop, function should really be a=b if context):-
require 'CSLSystem'

describe "LSystem" do
  context "given axiom 'baaaaaa'" do
    context "with function b=a" do
      describe "#generate" do
        it "should equal 'baaaaaa' at zero iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(0).should == 'baaaaaa'
        end
        it "should equal 'abaaaaa' after one iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'abaaaaa'
        end
        it "should equal 'aabaaaa' after one iteration" do
          LSystem.new('abaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(1).should == 'aabaaaa'
        end
        it "should equal 'aabaaaa' after two iterations" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(2).should == 'aabaaaa'
        end
        it "should equal 'aaabaaa' after three iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(3).should == 'aaabaaa'
        end
        it "should equal 'aaaaaba' after five iteration" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(5).should == 'aaaaaba'
        end
        it "should equal 'baaaaaa' after seven iterations" do
          LSystem.new('baaaaaa', {'b' => 'a', 'b<a' => 'b'}).generate(7).should == 'baaaaaa'
        end
      end
    end
  end
end


Here is some code designed to pass the rspec tests:-
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class LSystem

  attr_reader :axiom, :context, :no_context, :idx
  def initialize(axiom, rules)
    @axiom = axiom
    @no_context = {}
    @context = {}
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre if pre[1] == '>'
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = 0
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == "<"
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == prod[idx - 2] # use context sensitive rule
      elsif context[ch][1] == ">"
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == prod[idx] # use context sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end
end

Result 
.......


Finished in 0.00261 seconds 7 examples, 0 failures

Some more tests...
require 'CSLSystem'

describe "LSystem" do
  context "given axiom 'aaaaaaab'" do
    context "with function b=a" do
      describe "#generate" do
        it "should equal 'aaaaaaab' at zero iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(0).should == 'aaaaaaab'
        end
        it "should equal 'aaaaaba' after one iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaaaaba'
        end
        it "should equal 'aaaabaa' after one iteration" do
          LSystem.new('aaaaaaba', {'b' => 'a', 'a>b' => 'b'}).generate(1).should == 'aaaaabaa'
        end
        it "should equal ''aaaabaa'' after two iterations" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(2).should == 'aaaaabaa'
        end
        it "should equal 'aaabaaa' after three iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(3).should == 'aaaabaaa'
        end
        it "should equal 'abaaaaa' after five iteration" do
          LSystem.new('aaaaaaab', {'b' => 'a', 'a>b' => 'b'}).generate(5).should == 'aabaaaaa'
        end
        it "should equal 'aaaaaaab' after seven iterations" do
          LSystem.new('aaaaaaaa', {'b' => 'a', 'a>b' => 'b'}).generate(7).should == 'aaaaaaaa'
        end
      end
    end
  end
end


Here is the revised code to pass both sets of test...
######################################
# cs_grammar.rb a context sensitive
# 1-L lsystem grammar for 
# ruby/ruby-processing
# by Martin Prout (January 2013)
######################################



##################################
# The grammar class stores rules
# in two Hashes, one for cs rules,
# one for context free rules. Rules
# are filtered on input, and context
# is checked using get_rule in production
##################################

class LSystem

  attr_reader :axiom, :context, :no_context, :idx
  def initialize(axiom, rules)
    @axiom = axiom
    @no_context = {}
    @context = {}
    rules.each_pair do |pair|
      add_rule pair[0], pair[1]
    end
  end

  def add_rule(pre, rule)
    if pre.length == 3
      if pre[1] == '<'
        @context[pre[2]] = pre
      elsif pre[1] == '>'
        @context[pre[0]] = pre 
      end
      @no_context[pre] = rule # key length == 3
    elsif pre.length == 1
      @no_context[pre] = rule # key length == 1
    else
      print "unrecognized grammar '#{pre}'"
    end
  end

  def generate(repeat = 0) # repeat iteration grammar rules
    prod = axiom
    repeat.times do
      prod = new_production(prod)
    end
    return prod
  end


  def new_production prod  # single iteration grammar rules
    @idx = -1
    prod.gsub!(/./) do |ch|
      get_rule(prod, ch)
    end
  end

  def get_rule prod, ch
    rule = ch # default is return original character as rule (no change)
    @idx += 1 # increment the index of axiom/production as a side effect
    if (context.has_key?(ch))
      if context[ch][1] == '<'
        cs_char = context[ch][0]
        rule = no_context[context[ch]] if cs_char == prod[idx - 1] # use context sensitive rule
      elsif context[ch][1] == '>'
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == prod[idx + 1] # use context sensitive rule
      end
    else
      rule = no_context[ch] if no_context.has_key?(ch) # context free rule if it exists
    end
    return rule
  end
end

Followers

About Me

My photo
I have developed JRubyArt and propane new versions of ruby-processing for JRuby-9.1.5.0 and processing-3.2.2