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