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

Showing posts with label context sensitive lsystems. Show all posts
Showing posts with label context sensitive lsystems. Show all posts

Tuesday, 29 January 2013

Adding requirements rspec

In my previous post I presented an initial specification for my lsystems csgrammar parser. That is the most basic form, in which context is given by the preceding or following token in the axiom/production. Here is the specification of a more complicated parser that can take an ignore string, that includes those tokens that should be ignored wrt context. Typically the tokens will represent special instructions to interpreter such as [ and ] which mean push/pop matrix etc.
require 'CSLSystem'

IGNORE = 'ignore'

describe "LSystem" do
  context "given axiom 'biaaaaaa'" do
    context "with function a=b if context, ignoring IGNORE" do
      describe "#generate" do
        it "should equal 'biaaaaaa' at zero iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(0).should == 'biaaaaaa'
        end
        it "should equal 'aibaaaaa' after one iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'aibaaaaa'
        end
        it "should equal 'aiabaaaa' after one iteration" do
          LSystem.new('aibaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(1).should == 'aiabaaaa'
        end
        it "should equal 'aiabaaaa' after two iterations" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(2).should == 'aiabaaaa'
        end
        it "should equal 'aiaabaaa' after three iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(3).should == 'aiaabaaa'
        end
        it "should equal 'aiaaaaba' after five iteration" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(5).should == 'aiaaaaba'
        end
        it "should equal 'biaaaaaa' after seven iterations" do
          LSystem.new('biaaaaaa', {'b' => 'a', 'b<a' => 'b'}, IGNORE).generate(7).should == 'biaaaaaa'
        end
      end
    end
  end
end


Here is the revised grammar parser:-
######################################
# 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, :ignore
  def initialize(axiom, rules, ignore)
    @axiom = axiom
    @no_context = {}
    @context = {}
    @ignore = ignore
    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 == get_context(prod, idx, -1) # use context sensitive rule
      elsif context[ch][1] == '>'
        cs_char = context[ch][2]
        rule = no_context[context[ch]] if cs_char == get_context(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

  def get_context prod, idx, inc
    index = idx + inc
    while (ignore.include? prod[index])
      index += inc
    end
    return prod[index]
  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