gherkin 7.0.4 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/bin/gherkin +34 -12
- data/lib/gherkin.rb +42 -0
- data/lib/gherkin/ast_builder.rb +260 -0
- data/lib/gherkin/ast_node.rb +30 -0
- data/lib/gherkin/dialect.rb +2 -12
- data/lib/gherkin/errors.rb +45 -0
- data/lib/gherkin/gherkin-languages.json +3520 -0
- data/lib/gherkin/gherkin_line.rb +97 -0
- data/lib/gherkin/parser.rb +3429 -0
- data/lib/gherkin/pickles/compiler.rb +233 -0
- data/lib/gherkin/stream/parser_message_stream.rb +93 -0
- data/lib/gherkin/stream/protobuf_message_stream.rb +21 -0
- data/lib/gherkin/stream/subprocess_message_stream.rb +26 -0
- data/lib/gherkin/token.rb +18 -0
- data/lib/gherkin/token_formatter_builder.rb +39 -0
- data/lib/gherkin/token_matcher.rb +169 -0
- data/lib/gherkin/token_scanner.rb +40 -0
- data/spec/gherkin/gherkin_spec.rb +36 -345
- data/spec/gherkin/stream/subprocess_message_stream_spec.rb +18 -0
- metadata +28 -53
- data/executables/gherkin-darwin-386 +0 -0
- data/executables/gherkin-darwin-amd64 +0 -0
- data/executables/gherkin-freebsd-386 +0 -0
- data/executables/gherkin-freebsd-amd64 +0 -0
- data/executables/gherkin-freebsd-arm +0 -0
- data/executables/gherkin-linux-386 +0 -0
- data/executables/gherkin-linux-amd64 +0 -0
- data/executables/gherkin-linux-arm +0 -0
- data/executables/gherkin-linux-mips +0 -0
- data/executables/gherkin-linux-mips64 +0 -0
- data/executables/gherkin-linux-mips64le +0 -0
- data/executables/gherkin-linux-mipsle +0 -0
- data/executables/gherkin-linux-s390x +0 -0
- data/executables/gherkin-netbsd-386 +0 -0
- data/executables/gherkin-netbsd-amd64 +0 -0
- data/executables/gherkin-netbsd-arm +0 -0
- data/executables/gherkin-openbsd-386 +0 -0
- data/executables/gherkin-openbsd-amd64 +0 -0
- data/executables/gherkin-windows-386.exe +0 -0
- data/executables/gherkin-windows-amd64.exe +0 -0
- data/lib/gherkin/exe_file_path.rb +0 -3
- data/lib/gherkin/gherkin.rb +0 -72
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 03d427e820bf0e7c744cbe67f7f9173ade3a528dd7a3f02c94ef6b261d54fe7b
         | 
| 4 | 
            +
              data.tar.gz: 01d2c0fca9b02b4a0c6c480ab47c10178210814c86e7dd6f4ffcdde9540dcfda
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: c638788e4c6926086d5271cf414665d3fdaebf464643283238ca6f86cb000d1650c69017c5bc11260c2770a093b0c9538ab2ab0df73e44b2d9ef1c7e4f208475
         | 
| 7 | 
            +
              data.tar.gz: 4960487bbdde80f6d74df8e81fd4d3983a1fe2c998ea5d9842cfc8db9ab4607cf0c4909f5c89a2b1f19a0e8614eaabd0b673ed27d79488651da1caf51606cecb
         | 
    
        data/bin/gherkin
    CHANGED
    
    | @@ -4,12 +4,16 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"../lib")) | |
| 4 4 |  | 
| 5 5 | 
             
            require 'optparse'
         | 
| 6 6 | 
             
            require 'json'
         | 
| 7 | 
            -
            require ' | 
| 7 | 
            +
            require 'cucumber/messages'
         | 
| 8 | 
            +
            require 'gherkin'
         | 
| 9 | 
            +
            require 'gherkin/stream/subprocess_message_stream'
         | 
| 10 | 
            +
            require 'gherkin/stream/protobuf_message_stream'
         | 
| 8 11 |  | 
| 9 12 | 
             
            options = {
         | 
| 10 13 | 
             
              include_source: true,
         | 
| 11 14 | 
             
              include_gherkin_document: true,
         | 
| 12 | 
            -
              include_pickles: true
         | 
| 15 | 
            +
              include_pickles: true,
         | 
| 16 | 
            +
              json: false
         | 
| 13 17 | 
             
            }
         | 
| 14 18 |  | 
| 15 19 | 
             
            OptionParser.new do |opts|
         | 
| @@ -22,14 +26,21 @@ OptionParser.new do |opts| | |
| 22 26 | 
             
              opts.on("--[no-]pickles", "Don't print pickle messages") do |v|
         | 
| 23 27 | 
             
                options[:include_pickles] = v
         | 
| 24 28 | 
             
              end
         | 
| 29 | 
            +
              opts.on("--json", "Print messages as JSON") do |v|
         | 
| 30 | 
            +
                options[:json] = v
         | 
| 31 | 
            +
              end
         | 
| 25 32 | 
             
            end.parse!
         | 
| 26 33 |  | 
| 27 | 
            -
            def  | 
| 34 | 
            +
            def process_messages(messages, options)
         | 
| 28 35 | 
             
              messages.each do |message|
         | 
| 29 | 
            -
                json | 
| 30 | 
            -
             | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 36 | 
            +
                if options[:json]
         | 
| 37 | 
            +
                  json = message.class.encode_json(message)
         | 
| 38 | 
            +
                  ob = JSON.parse(json)
         | 
| 39 | 
            +
                  remove_empties(ob)
         | 
| 40 | 
            +
                  puts JSON.generate(ob)
         | 
| 41 | 
            +
                else
         | 
| 42 | 
            +
                  message.write_delimited_to(STDOUT)
         | 
| 43 | 
            +
                end
         | 
| 33 44 | 
             
              end
         | 
| 34 45 | 
             
            end
         | 
| 35 46 |  | 
| @@ -49,12 +60,23 @@ def remove_empties(ob) | |
| 49 60 | 
             
              end
         | 
| 50 61 | 
             
            end
         | 
| 51 62 |  | 
| 52 | 
            -
             | 
| 63 | 
            +
            gherkin_executable = ENV['GHERKIN_EXECUTABLE']
         | 
| 64 | 
            +
            if ARGV.empty?
         | 
| 65 | 
            +
              # Read protobuf from STDIN
         | 
| 66 | 
            +
              messages = Gherkin::Stream::ProtobufMessageStream.new(STDIN).messages
         | 
| 67 | 
            +
            elsif gherkin_executable
         | 
| 53 68 | 
             
              # Read protobuf from STDIN
         | 
| 54 | 
            -
              Gherkin:: | 
| 55 | 
            -
             | 
| 56 | 
            -
             | 
| 69 | 
            +
              messages = Gherkin::Stream::SubprocessMessageStream.new(
         | 
| 70 | 
            +
                gherkin_executable,
         | 
| 71 | 
            +
                ARGV,
         | 
| 72 | 
            +
                options[:include_source],
         | 
| 73 | 
            +
                options[:include_gherkin_document],
         | 
| 74 | 
            +
                options[:include_pickles]
         | 
| 75 | 
            +
              ).messages
         | 
| 76 | 
            +
            else
         | 
| 77 | 
            +
              messages = Gherkin.from_paths(
         | 
| 57 78 | 
             
                ARGV,
         | 
| 58 79 | 
             
                options
         | 
| 59 80 | 
             
              )
         | 
| 60 | 
            -
             | 
| 81 | 
            +
            end
         | 
| 82 | 
            +
            process_messages(messages, options)
         | 
    
        data/lib/gherkin.rb
    ADDED
    
    | @@ -0,0 +1,42 @@ | |
| 1 | 
            +
            require 'gherkin/stream/parser_message_stream'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Gherkin
         | 
| 4 | 
            +
              DEFAULT_OPTIONS = {
         | 
| 5 | 
            +
                include_source: true,
         | 
| 6 | 
            +
                include_gherkin_document: true,
         | 
| 7 | 
            +
                include_pickles: true
         | 
| 8 | 
            +
              }.freeze
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def self.from_paths(paths, options={})
         | 
| 11 | 
            +
                Stream::ParserMessageStream.new(
         | 
| 12 | 
            +
                    paths,
         | 
| 13 | 
            +
                    [],
         | 
| 14 | 
            +
                    options
         | 
| 15 | 
            +
                ).messages
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              def self.from_sources(sources, options={})
         | 
| 19 | 
            +
                Stream::ParserMessageStream.new(
         | 
| 20 | 
            +
                    [],
         | 
| 21 | 
            +
                    sources,
         | 
| 22 | 
            +
                    options
         | 
| 23 | 
            +
                ).messages
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              def self.from_source(uri, data, options={})
         | 
| 27 | 
            +
                from_sources([encode_source_message(uri, data)], options)
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              private
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def self.encode_source_message(uri, data)
         | 
| 33 | 
            +
                Cucumber::Messages::Source.new({
         | 
| 34 | 
            +
                  uri: uri,
         | 
| 35 | 
            +
                  data: data,
         | 
| 36 | 
            +
                  media: Cucumber::Messages::Media.new({
         | 
| 37 | 
            +
                    encoding: :UTF8,
         | 
| 38 | 
            +
                    content_type: 'text/x.cucumber.gherkin+plain'
         | 
| 39 | 
            +
                  })
         | 
| 40 | 
            +
                })
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
            end
         | 
| @@ -0,0 +1,260 @@ | |
| 1 | 
            +
            require 'cucumber/messages'
         | 
| 2 | 
            +
            require 'gherkin/ast_node'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module Gherkin
         | 
| 5 | 
            +
              class AstBuilder
         | 
| 6 | 
            +
                def initialize
         | 
| 7 | 
            +
                  reset
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def reset
         | 
| 11 | 
            +
                  @stack = [AstNode.new(:None)]
         | 
| 12 | 
            +
                  @comments = []
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                def start_rule(rule_type)
         | 
| 16 | 
            +
                  @stack.push AstNode.new(rule_type)
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                def end_rule(rule_type)
         | 
| 20 | 
            +
                  node = @stack.pop
         | 
| 21 | 
            +
                  current_node.add(node.rule_type, transform_node(node))
         | 
| 22 | 
            +
                end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def build(token)
         | 
| 25 | 
            +
                  if token.matched_type == :Comment
         | 
| 26 | 
            +
                    @comments.push(Cucumber::Messages::GherkinDocument::Comment.new(
         | 
| 27 | 
            +
                      location: get_location(token, 0),
         | 
| 28 | 
            +
                      text: token.matched_text
         | 
| 29 | 
            +
                    ))
         | 
| 30 | 
            +
                  else
         | 
| 31 | 
            +
                    current_node.add(token.matched_type, token)
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                def get_result
         | 
| 36 | 
            +
                  current_node.get_single(:GherkinDocument)
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                def current_node
         | 
| 40 | 
            +
                  @stack.last
         | 
| 41 | 
            +
                end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                def get_location(token, column)
         | 
| 44 | 
            +
                  column = column == 0 ? token.location[:column] : column
         | 
| 45 | 
            +
                  Cucumber::Messages::Location.new(
         | 
| 46 | 
            +
                    line: token.location[:line],
         | 
| 47 | 
            +
                    column: column
         | 
| 48 | 
            +
                  )
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                def get_tags(node)
         | 
| 52 | 
            +
                  tags = []
         | 
| 53 | 
            +
                  tags_node = node.get_single(:Tags)
         | 
| 54 | 
            +
                  return tags unless tags_node
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                  tags_node.get_tokens(:TagLine).each do |token|
         | 
| 57 | 
            +
                    token.matched_items.each do |tag_item|
         | 
| 58 | 
            +
                      tags.push(Cucumber::Messages::GherkinDocument::Feature::Tag.new(
         | 
| 59 | 
            +
                        location: get_location(token, tag_item.column),
         | 
| 60 | 
            +
                        name: tag_item.text
         | 
| 61 | 
            +
                      ))
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  tags
         | 
| 66 | 
            +
                end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                def get_table_rows(node)
         | 
| 69 | 
            +
                  rows = node.get_tokens(:TableRow).map do |token|
         | 
| 70 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::TableRow.new(
         | 
| 71 | 
            +
                      location: get_location(token, 0),
         | 
| 72 | 
            +
                      cells: get_cells(token)
         | 
| 73 | 
            +
                    )
         | 
| 74 | 
            +
                  end
         | 
| 75 | 
            +
                  ensure_cell_count(rows)
         | 
| 76 | 
            +
                  rows
         | 
| 77 | 
            +
                end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                def ensure_cell_count(rows)
         | 
| 80 | 
            +
                  return if rows.empty?
         | 
| 81 | 
            +
                  cell_count = rows[0].cells.length
         | 
| 82 | 
            +
                  rows.each do |row|
         | 
| 83 | 
            +
                    if row.cells.length != cell_count
         | 
| 84 | 
            +
                      location = {line: row.location.line, column: row.location.column}
         | 
| 85 | 
            +
                      raise AstBuilderException.new("inconsistent cell count within the table", location)
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                def get_cells(table_row_token)
         | 
| 91 | 
            +
                  table_row_token.matched_items.map do |cell_item|
         | 
| 92 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::TableRow::TableCell.new(
         | 
| 93 | 
            +
                      location: get_location(table_row_token, cell_item.column),
         | 
| 94 | 
            +
                      value: cell_item.text
         | 
| 95 | 
            +
                    )
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                def get_description(node)
         | 
| 100 | 
            +
                  node.get_single(:Description)
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                def get_steps(node)
         | 
| 104 | 
            +
                  node.get_items(:Step)
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                def transform_node(node)
         | 
| 108 | 
            +
                  case node.rule_type
         | 
| 109 | 
            +
                  when :Step
         | 
| 110 | 
            +
                    step_line = node.get_token(:StepLine)
         | 
| 111 | 
            +
                    data_table = node.get_single(:DataTable)
         | 
| 112 | 
            +
                    doc_string = node.get_single(:DocString)
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                    props = {
         | 
| 115 | 
            +
                      location: get_location(step_line, 0),
         | 
| 116 | 
            +
                      keyword: step_line.matched_keyword,
         | 
| 117 | 
            +
                      text: step_line.matched_text
         | 
| 118 | 
            +
                    }
         | 
| 119 | 
            +
                    props[:data_table] = data_table if data_table
         | 
| 120 | 
            +
                    props[:doc_string] = doc_string if doc_string
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Step.new(props)
         | 
| 123 | 
            +
                  when :DocString
         | 
| 124 | 
            +
                    separator_token = node.get_tokens(:DocStringSeparator)[0]
         | 
| 125 | 
            +
                    content_type = separator_token.matched_text == '' ? nil : separator_token.matched_text
         | 
| 126 | 
            +
                    line_tokens = node.get_tokens(:Other)
         | 
| 127 | 
            +
                    content = line_tokens.map { |t| t.matched_text }.join("\n")
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                    props = {
         | 
| 130 | 
            +
                      location: get_location(separator_token, 0),
         | 
| 131 | 
            +
                      content: content,
         | 
| 132 | 
            +
                      delimiter: separator_token.matched_keyword
         | 
| 133 | 
            +
                    }
         | 
| 134 | 
            +
                    props[:content_type] = content_type if content_type
         | 
| 135 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Step::DocString.new(props)
         | 
| 136 | 
            +
                  when :DataTable
         | 
| 137 | 
            +
                    rows = get_table_rows(node)
         | 
| 138 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Step::DataTable.new(
         | 
| 139 | 
            +
                      location: rows[0].location,
         | 
| 140 | 
            +
                      rows: rows,
         | 
| 141 | 
            +
                    )
         | 
| 142 | 
            +
                  when :Background
         | 
| 143 | 
            +
                    background_line = node.get_token(:BackgroundLine)
         | 
| 144 | 
            +
                    description = get_description(node)
         | 
| 145 | 
            +
                    steps = get_steps(node)
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    props = {
         | 
| 148 | 
            +
                      location: get_location(background_line, 0),
         | 
| 149 | 
            +
                      keyword: background_line.matched_keyword,
         | 
| 150 | 
            +
                      name: background_line.matched_text,
         | 
| 151 | 
            +
                      steps: steps
         | 
| 152 | 
            +
                    }
         | 
| 153 | 
            +
                    props[:description] = description if description
         | 
| 154 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Background.new(props)
         | 
| 155 | 
            +
                  when :ScenarioDefinition
         | 
| 156 | 
            +
                    tags = get_tags(node)
         | 
| 157 | 
            +
                    scenario_node = node.get_single(:Scenario)
         | 
| 158 | 
            +
                    scenario_line = scenario_node.get_token(:ScenarioLine)
         | 
| 159 | 
            +
                    description = get_description(scenario_node)
         | 
| 160 | 
            +
                    steps = get_steps(scenario_node)
         | 
| 161 | 
            +
                    examples = scenario_node.get_items(:ExamplesDefinition)
         | 
| 162 | 
            +
                    props = {
         | 
| 163 | 
            +
                      tags: tags,
         | 
| 164 | 
            +
                      location: get_location(scenario_line, 0),
         | 
| 165 | 
            +
                      keyword: scenario_line.matched_keyword,
         | 
| 166 | 
            +
                      name: scenario_line.matched_text,
         | 
| 167 | 
            +
                      steps: steps,
         | 
| 168 | 
            +
                      examples: examples
         | 
| 169 | 
            +
                    }
         | 
| 170 | 
            +
                    props[:description] = description if description
         | 
| 171 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Scenario.new(props)
         | 
| 172 | 
            +
                  when :ExamplesDefinition
         | 
| 173 | 
            +
                    tags = get_tags(node)
         | 
| 174 | 
            +
                    examples_node = node.get_single(:Examples)
         | 
| 175 | 
            +
                    examples_line = examples_node.get_token(:ExamplesLine)
         | 
| 176 | 
            +
                    description = get_description(examples_node)
         | 
| 177 | 
            +
                    rows = examples_node.get_single(:ExamplesTable)
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    table_header = rows.nil? ? nil : rows.first
         | 
| 180 | 
            +
                    table_body = rows.nil? ? nil : rows[1..-1]
         | 
| 181 | 
            +
             | 
| 182 | 
            +
                    props = {
         | 
| 183 | 
            +
                      tags: tags,
         | 
| 184 | 
            +
                      location: get_location(examples_line, 0),
         | 
| 185 | 
            +
                      keyword: examples_line.matched_keyword,
         | 
| 186 | 
            +
                      name: examples_line.matched_text,
         | 
| 187 | 
            +
                    }
         | 
| 188 | 
            +
                    props[:table_header] = table_header if table_header
         | 
| 189 | 
            +
                    props[:table_body] = table_body if table_body
         | 
| 190 | 
            +
                    props[:description] = description if description
         | 
| 191 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::Scenario::Examples.new(props)
         | 
| 192 | 
            +
                  when :ExamplesTable
         | 
| 193 | 
            +
                    get_table_rows(node)
         | 
| 194 | 
            +
                  when :Description
         | 
| 195 | 
            +
                    line_tokens = node.get_tokens(:Other)
         | 
| 196 | 
            +
                    # Trim trailing empty lines
         | 
| 197 | 
            +
                    last_non_empty = line_tokens.rindex { |token| !token.line.trimmed_line_text.empty? }
         | 
| 198 | 
            +
                    description = line_tokens[0..last_non_empty].map { |token| token.matched_text }.join("\n")
         | 
| 199 | 
            +
                    return description
         | 
| 200 | 
            +
                  when :Feature
         | 
| 201 | 
            +
                    header = node.get_single(:FeatureHeader)
         | 
| 202 | 
            +
                    return unless header
         | 
| 203 | 
            +
                    tags = get_tags(header)
         | 
| 204 | 
            +
                    feature_line = header.get_token(:FeatureLine)
         | 
| 205 | 
            +
                    return unless feature_line
         | 
| 206 | 
            +
                    children = []
         | 
| 207 | 
            +
                    background = node.get_single(:Background)
         | 
| 208 | 
            +
                    children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(background: background)) if background
         | 
| 209 | 
            +
                    node.get_items(:ScenarioDefinition).each do |scenario|
         | 
| 210 | 
            +
                      children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(scenario: scenario))
         | 
| 211 | 
            +
                    end
         | 
| 212 | 
            +
                    node.get_items(:Rule).each do |rule|
         | 
| 213 | 
            +
                      children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild.new(rule: rule))
         | 
| 214 | 
            +
                    end
         | 
| 215 | 
            +
                    description = get_description(header)
         | 
| 216 | 
            +
                    language = feature_line.matched_gherkin_dialect
         | 
| 217 | 
            +
             | 
| 218 | 
            +
                    props = {
         | 
| 219 | 
            +
                      tags: tags,
         | 
| 220 | 
            +
                      location: get_location(feature_line, 0),
         | 
| 221 | 
            +
                      language: language,
         | 
| 222 | 
            +
                      keyword: feature_line.matched_keyword,
         | 
| 223 | 
            +
                      name: feature_line.matched_text,
         | 
| 224 | 
            +
                      children: children,
         | 
| 225 | 
            +
                    }
         | 
| 226 | 
            +
                    props[:description] = description if description
         | 
| 227 | 
            +
             | 
| 228 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature.new(props)
         | 
| 229 | 
            +
                  when :Rule
         | 
| 230 | 
            +
                    header = node.get_single(:RuleHeader)
         | 
| 231 | 
            +
                    return unless header
         | 
| 232 | 
            +
                    rule_line = header.get_token(:RuleLine)
         | 
| 233 | 
            +
                    return unless rule_line
         | 
| 234 | 
            +
                    children = []
         | 
| 235 | 
            +
                    background = node.get_single(:Background)
         | 
| 236 | 
            +
                    children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild::RuleChild.new(background: background)) if background
         | 
| 237 | 
            +
                    node.get_items(:ScenarioDefinition).each do |scenario|
         | 
| 238 | 
            +
                      children.push(Cucumber::Messages::GherkinDocument::Feature::FeatureChild::RuleChild.new(scenario: scenario))
         | 
| 239 | 
            +
                    end
         | 
| 240 | 
            +
                    description = get_description(header)
         | 
| 241 | 
            +
             | 
| 242 | 
            +
                    Cucumber::Messages::GherkinDocument::Feature::FeatureChild::Rule.new(
         | 
| 243 | 
            +
                      location: get_location(rule_line, 0),
         | 
| 244 | 
            +
                      keyword: rule_line.matched_keyword,
         | 
| 245 | 
            +
                      name: rule_line.matched_text,
         | 
| 246 | 
            +
                      description: description,
         | 
| 247 | 
            +
                      children: children,
         | 
| 248 | 
            +
                    )
         | 
| 249 | 
            +
                  when :GherkinDocument
         | 
| 250 | 
            +
                    feature = node.get_single(:Feature)
         | 
| 251 | 
            +
                    {
         | 
| 252 | 
            +
                      feature: feature,
         | 
| 253 | 
            +
                      comments: @comments
         | 
| 254 | 
            +
                    }
         | 
| 255 | 
            +
                  else
         | 
| 256 | 
            +
                    return node
         | 
| 257 | 
            +
                  end
         | 
| 258 | 
            +
                end
         | 
| 259 | 
            +
              end
         | 
| 260 | 
            +
            end
         | 
| @@ -0,0 +1,30 @@ | |
| 1 | 
            +
            module Gherkin
         | 
| 2 | 
            +
              class AstNode
         | 
| 3 | 
            +
                attr_reader :rule_type
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize(rule_type)
         | 
| 6 | 
            +
                  @rule_type = rule_type
         | 
| 7 | 
            +
                  @_sub_items = Hash.new { |hash, key| hash[key] = [] } # returns [] for unknown key
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                def add(rule_type, obj)
         | 
| 11 | 
            +
                  @_sub_items[rule_type].push(obj)
         | 
| 12 | 
            +
                end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                def get_single(rule_type)
         | 
| 15 | 
            +
                  @_sub_items[rule_type].first
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def get_items(rule_type)
         | 
| 19 | 
            +
                  @_sub_items[rule_type]
         | 
| 20 | 
            +
                end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                def get_token(token_type)
         | 
| 23 | 
            +
                  get_single(token_type)
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                def get_tokens(token_type)
         | 
| 27 | 
            +
                  @_sub_items[token_type]
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
            end
         | 
    
        data/lib/gherkin/dialect.rb
    CHANGED
    
    | @@ -1,18 +1,8 @@ | |
| 1 1 | 
             
            require 'json'
         | 
| 2 | 
            -
            require 'open3'
         | 
| 3 | 
            -
            require 'c21e/exe_file'
         | 
| 4 | 
            -
            require 'gherkin/exe_file_path'
         | 
| 5 2 |  | 
| 6 3 | 
             
            module Gherkin
         | 
| 7 | 
            -
               | 
| 8 | 
            -
             | 
| 9 | 
            -
                data, = Open3.capture2e(gherkin_executable, '--dialects')
         | 
| 10 | 
            -
                data
         | 
| 11 | 
            -
              end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
              private_class_method :dialects_json
         | 
| 14 | 
            -
             | 
| 15 | 
            -
              DIALECTS = JSON.parse(dialects_json)
         | 
| 4 | 
            +
              DIALECT_FILE_PATH = File.expand_path("gherkin-languages.json", File.dirname(__FILE__))
         | 
| 5 | 
            +
              DIALECTS = JSON.parse File.open(DIALECT_FILE_PATH, 'r:UTF-8').read
         | 
| 16 6 |  | 
| 17 7 | 
             
              class Dialect
         | 
| 18 8 | 
             
                def self.for(name)
         | 
| @@ -0,0 +1,45 @@ | |
| 1 | 
            +
            module Gherkin
         | 
| 2 | 
            +
              class ParserError < StandardError; end
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              class ParserException < ParserError
         | 
| 5 | 
            +
                attr_reader :location
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                def initialize(message, location)
         | 
| 8 | 
            +
                  @location = location
         | 
| 9 | 
            +
                  super("(#{location[:line]}:#{location[:column] || 0}): #{message}")
         | 
| 10 | 
            +
                end
         | 
| 11 | 
            +
              end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
              class NoSuchLanguageException < ParserException
         | 
| 14 | 
            +
                def initialize(language, location)
         | 
| 15 | 
            +
                  super "Language not supported: #{language}", location
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              class AstBuilderException < ParserException; end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
              class CompositeParserException < ParserError
         | 
| 22 | 
            +
                attr_reader :errors
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                def initialize(errors)
         | 
| 25 | 
            +
                  @errors = errors
         | 
| 26 | 
            +
                  super "Parser errors:\n" + errors.map(&:message).join("\n")
         | 
| 27 | 
            +
                end
         | 
| 28 | 
            +
              end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              class UnexpectedTokenException < ParserException
         | 
| 31 | 
            +
                def initialize(received_token, expected_token_types, state_comment)
         | 
| 32 | 
            +
                  message = "expected: #{expected_token_types.join(", ")}, got '#{received_token.token_value.strip}'"
         | 
| 33 | 
            +
                  column = received_token.location[:column]
         | 
| 34 | 
            +
                  location =  (column.nil? || column.zero?) ? {line: received_token.location[:line], column: received_token.line.indent + 1} : received_token.location
         | 
| 35 | 
            +
                  super(message, location)
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              class UnexpectedEOFException < ParserException
         | 
| 40 | 
            +
                def initialize(received_token, expected_token_types, state_comment)
         | 
| 41 | 
            +
                  message = "unexpected end of file, expected: #{expected_token_types.join(", ")}"
         | 
| 42 | 
            +
                  super(message, received_token.location)
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         |