--- /dev/null
+module Spec
+ module Story
+ module Runner
+
+ class IllegalStepError < StandardError
+ def initialize(state, event)
+ super("Illegal attempt to create a #{event} after a #{state}")
+ end
+ end
+
+ class StoryParser
+ def initialize(story_mediator)
+ @story_mediator = story_mediator
+ @current_story_lines = []
+ transition_to(:starting_state)
+ end
+
+ def parse(lines)
+ lines.reject! {|line| line == ""}
+ until lines.empty?
+ process_line(lines.shift)
+ end
+ @state.eof
+ end
+
+ def process_line(line)
+ line.strip!
+ case line
+ when /^Story: / then @state.story(line)
+ when /^Scenario: / then @state.scenario(line)
+ when /^Given:? / then @state.given(line)
+ when /^GivenScenario:? / then @state.given_scenario(line)
+ when /^When:? / then @state.event(line)
+ when /^Then:? / then @state.outcome(line)
+ when /^And:? / then @state.one_more_of_the_same(line)
+ else @state.other(line)
+ end
+ end
+
+ def init_story(title)
+ @current_story_lines.clear
+ add_story_line(title)
+ end
+
+ def add_story_line(line)
+ @current_story_lines << line
+ end
+
+ def create_story()
+ unless @current_story_lines.empty?
+ @story_mediator.create_story(@current_story_lines[0].gsub("Story: ",""), @current_story_lines[1..-1].join("\n"))
+ @current_story_lines.clear
+ end
+ end
+
+ def create_scenario(title)
+ @story_mediator.create_scenario(title.gsub("Scenario: ",""))
+ end
+
+ def create_given(name)
+ @story_mediator.create_given(name)
+ end
+
+ def create_given_scenario(name)
+ @story_mediator.create_given_scenario(name)
+ end
+
+ def create_when(name)
+ @story_mediator.create_when(name)
+ end
+
+ def create_then(name)
+ @story_mediator.create_then(name)
+ end
+
+ def transition_to(key)
+ @state = states[key]
+ end
+
+ def states
+ @states ||= {
+ :starting_state => StartingState.new(self),
+ :story_state => StoryState.new(self),
+ :scenario_state => ScenarioState.new(self),
+ :given_state => GivenState.new(self),
+ :when_state => WhenState.new(self),
+ :then_state => ThenState.new(self)
+ }
+ end
+
+ class State
+ def initialize(parser)
+ @parser = parser
+ end
+
+ def story(line)
+ @parser.init_story(line)
+ @parser.transition_to(:story_state)
+ end
+
+ def scenario(line)
+ @parser.create_scenario(line)
+ @parser.transition_to(:scenario_state)
+ end
+
+ def given(line)
+ @parser.create_given(remove_tag_from(:given, line))
+ @parser.transition_to(:given_state)
+ end
+
+ def given_scenario(line)
+ @parser.create_given_scenario(remove_tag_from(:givenscenario, line))
+ @parser.transition_to(:given_state)
+ end
+
+ def event(line)
+ @parser.create_when(remove_tag_from(:when, line))
+ @parser.transition_to(:when_state)
+ end
+
+ def outcome(line)
+ @parser.create_then(remove_tag_from(:then, line))
+ @parser.transition_to(:then_state)
+ end
+
+ def remove_tag_from(tag, line)
+ tokens = line.split
+ # validation of tag can go here
+ tokens[0].downcase.match(/#{tag.to_s}:?/) ?
+ (tokens[1..-1].join(' ')) : line
+ end
+
+ def eof
+ end
+
+ def other(line)
+ # no-op - supports header text before the first story in a file
+ end
+ end
+
+ class StartingState < State
+ def initialize(parser)
+ @parser = parser
+ end
+ end
+
+ class StoryState < State
+ def one_more_of_the_same(line)
+ other(line)
+ end
+
+ def story(line)
+ @parser.create_story
+ @parser.add_story_line(line)
+ end
+
+ def scenario(line)
+ @parser.create_story
+ @parser.create_scenario(line)
+ @parser.transition_to(:scenario_state)
+ end
+
+ def given(line)
+ other(line)
+ end
+
+ def event(line)
+ other(line)
+ end
+
+ def outcome(line)
+ other(line)
+ end
+
+ def other(line)
+ @parser.add_story_line(line)
+ end
+
+ def eof
+ @parser.create_story
+ end
+ end
+
+ class ScenarioState < State
+ def one_more_of_the_same(line)
+ raise IllegalStepError.new("Scenario", "And")
+ end
+
+ def scenario(line)
+ @parser.create_scenario(line)
+ end
+ end
+
+ class GivenState < State
+ def one_more_of_the_same(line)
+ @parser.create_given(remove_tag_from(:and, line))
+ end
+
+ def given(line)
+ @parser.create_given(remove_tag_from(:given, line))
+ end
+ end
+
+ class WhenState < State
+ def one_more_of_the_same(line)
+ @parser.create_when(remove_tag_from(:and ,line))
+ end
+
+ def event(line)
+ @parser.create_when(remove_tag_from(:when ,line))
+ end
+ end
+
+ class ThenState < State
+ def one_more_of_the_same(line)
+ @parser.create_then(remove_tag_from(:and ,line))
+ end
+
+ def outcome(line)
+ @parser.create_then(remove_tag_from(:then ,line))
+ end
+ end
+
+ end
+ end
+ end
+end