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:-
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
elsif pre.length == 1
@no_context[pre] = rule
else
print "unrecognized grammar '#{pre}'"
end
end
def generate(repeat = 0)
prod = axiom
repeat.times do
prod = new_production(prod)
end
return prod
end
def new_production prod
@idx = 0
prod.gsub!(/./) do |ch|
get_rule(prod, ch)
end
end
def get_rule prod, ch
rule = ch
@idx += 1
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]
elsif context[ch][1] == ">"
cs_char = context[ch][2]
rule = no_context[context[ch]] if cs_char == prod[idx]
end
else
rule = no_context[ch] if no_context.has_key?(ch)
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...
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
elsif pre.length == 1
@no_context[pre] = rule
else
print "unrecognized grammar '#{pre}'"
end
end
def generate(repeat = 0)
prod = axiom
repeat.times do
prod = new_production(prod)
end
return prod
end
def new_production prod
@idx = -1
prod.gsub!(/./) do |ch|
get_rule(prod, ch)
end
end
def get_rule prod, ch
rule = ch
@idx += 1
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]
elsif context[ch][1] == '>'
cs_char = context[ch][2]
rule = no_context[context[ch]] if cs_char == prod[idx + 1]
end
else
rule = no_context[ch] if no_context.has_key?(ch)
end
return rule
end
end