module Cucumber module Parser # TIP: When you hack on the grammar, just delete feature.rb in this directory. # Treetop will then generate the parser in-memory. When you're happy, just generate # the rb file with tt feature.tt grammar Feature include FileParser include I18n rule feature white comment white tags white header:(!(scenario_outline / scenario) .)* feature_elements { def build Ast::Feature.new(comment.build, tags.build, header.text_value, feature_elements.build) end } end rule tags white ts:(tag (space/eol)+)* { def build tag_names = ts.elements.map{|e| e.tag.tag_name.text_value} Ast::Tags.new(ts.line, tag_names) end } end rule tag '@' tag_name:([a-z0-9])+ end rule comment (comment_line eol+)* { def build Ast::Comment.new(text_value) end } end rule comment_line '#' line_to_eol end rule feature_elements (scenario / scenario_outline)* { def build elements.map{|s| s.build} end } end rule scenario comment tags white scenario_keyword space* name:line_to_eol (eol+ / eof) steps { def build Ast::Scenario.new( comment.build, tags.build, scenario_keyword.line, scenario_keyword.text_value, name.text_value, steps.build ) end } end rule scenario_outline comment tags white scenario_outline_keyword space* name:line_to_eol white steps examples_sections { def build Ast::ScenarioOutline.new( comment.build, tags.build, scenario_outline_keyword.line, scenario_outline_keyword.text_value, name.text_value, steps.build, examples_sections.build ) end } end rule steps step* { def build elements.map{|e| e.build} end } end rule step space* step_keyword space* name:line_to_eol (eol+ / eof) multi:multiline_arg? { def build if multi.respond_to?(:build) Ast::Step.new(step_keyword.line, step_keyword.text_value, name.text_value, multi.build) else Ast::Step.new(step_keyword.line, step_keyword.text_value, name.text_value) end end } end rule examples_sections examples+ { def build elements.map{|e| e.build} end } end rule examples space* examples_keyword white table { def build [examples_keyword.line, examples_keyword.text_value, "", table.raw] end } end rule multiline_arg table / py_string end rule table table_row+ { def build Ast::Table.new(raw) end def raw elements.map{|e| e.build} end } end rule table_row space* '|' cells:(cell '|')+ space* (eol+ / eof) { def build row = cells.elements.map do |elt| value = elt.cell.text_value.strip value.empty? ? nil : value end class << row attr_accessor :line end row.line = cells.line row end } end rule cell (!('|' / eol) .)* end rule line_to_eol (!eol .)+ end rule py_string open_py_string s:(!close_py_string .)* close_py_string { def build Ast::PyString.new(open_py_string.line, close_py_string.line, s.text_value, open_py_string.indentation) end } end rule open_py_string white '"""' space* eol { def indentation white.text_value.length end def line white.line end } end rule close_py_string eol space* quotes:'"""' white { def line quotes.line end } end rule white (space / eol)* end rule space [ \t] end rule eol "\n" / ("\r" "\n"?) end rule eof !. end end end end