gherkin3 3.0.0.alpha.1

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.
@@ -0,0 +1,18 @@
1
+ module Gherkin3
2
+ class Token < Struct.new(:line, :location)
3
+ attr_accessor :matched_type, :matched_text, :matched_keyword, :matched_indent,
4
+ :matched_items, :matched_gherkin_dialect
5
+
6
+ def eof?
7
+ line.nil?
8
+ end
9
+
10
+ def detach
11
+ # TODO: detach line - is this needed?
12
+ end
13
+
14
+ def token_value
15
+ eof? ? "EOF" : line.get_line_text(-1)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,35 @@
1
+ module Gherkin3
2
+ class TokenFormatterBuilder
3
+ def initialize
4
+ @tokens_text = ""
5
+ end
6
+
7
+ def build(token)
8
+ @tokens_text << "#{format_token(token)}\n"
9
+ end
10
+
11
+ def start_rule(rule_type)
12
+ end
13
+
14
+ def end_rule(rule_type)
15
+ end
16
+
17
+ def get_result
18
+ @tokens_text
19
+ end
20
+
21
+ private
22
+ def format_token(token)
23
+ return "EOF" if token.eof?
24
+
25
+ sprintf "(%s:%s)%s:%s/%s/%s",
26
+ token.location[:line],
27
+ token.location[:column],
28
+ token.matched_type,
29
+ token.matched_keyword,
30
+ token.matched_text,
31
+ Array(token.matched_items).map { |i| "#{i.column}:#{i.text}"}.join(',')
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,163 @@
1
+ require_relative 'dialect'
2
+ require_relative 'errors'
3
+
4
+ module Gherkin3
5
+ class TokenMatcher
6
+ LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-_]+)\s*$/
7
+
8
+ def initialize(dialect_name = 'en')
9
+ change_dialect(dialect_name, nil)
10
+ @active_doc_string_separator = nil
11
+ @indent_to_remove = 0
12
+ end
13
+
14
+ def match_TagLine(token)
15
+ return false unless token.line.start_with?('@')
16
+
17
+ set_token_matched(token, :TagLine, nil, nil, nil, token.line.tags)
18
+ true
19
+ end
20
+
21
+ def match_FeatureLine(token)
22
+ match_title_line(token, :FeatureLine, @dialect.feature_keywords)
23
+ end
24
+
25
+ def match_ScenarioLine(token)
26
+ match_title_line(token, :ScenarioLine, @dialect.scenario_keywords)
27
+ end
28
+
29
+ def match_ScenarioOutlineLine(token)
30
+ match_title_line(token, :ScenarioOutlineLine, @dialect.scenario_outline_keywords)
31
+ end
32
+
33
+ def match_BackgroundLine(token)
34
+ match_title_line(token, :BackgroundLine, @dialect.background_keywords)
35
+ end
36
+
37
+ def match_ExamplesLine(token)
38
+ match_title_line(token, :ExamplesLine, @dialect.examples_keywords)
39
+ end
40
+
41
+ def match_TableRow(token)
42
+ return false unless token.line.start_with?('|')
43
+ # TODO: indent
44
+ set_token_matched(token, :TableRow, nil, nil, nil, token.line.table_cells)
45
+ true
46
+ end
47
+
48
+ def match_Empty(token)
49
+ return false unless token.line.empty?
50
+ set_token_matched(token, :Empty, nil, nil, 0)
51
+ true
52
+ end
53
+
54
+ def match_Comment(token)
55
+ return false unless token.line.start_with?('#')
56
+ text = token.line.get_line_text(0) #take the entire line, including leading space
57
+ set_token_matched(token, :Comment, text, nil, 0)
58
+ true
59
+ end
60
+
61
+ def match_Language(token)
62
+ return false unless token.line.trimmed_line_text =~ LANGUAGE_PATTERN
63
+
64
+ dialect_name = $1
65
+ set_token_matched(token, :Language, dialect_name)
66
+
67
+ change_dialect(dialect_name, token.location)
68
+
69
+ true
70
+ end
71
+
72
+ def match_DocStringSeparator(token)
73
+ if @active_doc_string_separator.nil?
74
+ # open
75
+ _match_DocStringSeparator(token, '"""', true) ||
76
+ _match_DocStringSeparator(token, '```', true)
77
+ else
78
+ # close
79
+ _match_DocStringSeparator(token, @active_doc_string_separator, false)
80
+ end
81
+ end
82
+
83
+ def _match_DocStringSeparator(token, separator, is_open)
84
+ return false unless token.line.start_with?(separator)
85
+
86
+ content_type = nil
87
+ if is_open
88
+ content_type = token.line.get_rest_trimmed(separator.length)
89
+ @active_doc_string_separator = separator
90
+ @indent_to_remove = token.line.indent
91
+ else
92
+ @active_doc_string_separator = nil
93
+ @indent_to_remove = 0
94
+ end
95
+
96
+ # TODO: Use the separator as keyword. That's needed for pretty printing.
97
+ set_token_matched(token, :DocStringSeparator, content_type)
98
+ true
99
+ end
100
+
101
+ def match_EOF(token)
102
+ return false unless token.eof?
103
+ set_token_matched(token, :EOF)
104
+ true
105
+ end
106
+
107
+ def match_Other(token)
108
+ text = token.line.get_line_text(@indent_to_remove) # take the entire line, except removing DocString indents
109
+ set_token_matched(token, :Other, unescape_docstring(text), nil, 0)
110
+ true
111
+ end
112
+
113
+ def match_StepLine(token)
114
+ keywords = @dialect.given_keywords +
115
+ @dialect.when_keywords +
116
+ @dialect.then_keywords +
117
+ @dialect.and_keywords +
118
+ @dialect.but_keywords
119
+
120
+ keyword = keywords.detect { |k| token.line.start_with?(k) }
121
+
122
+ return false unless keyword
123
+
124
+ title = token.line.get_rest_trimmed(keyword.length)
125
+ set_token_matched(token, :StepLine, title, keyword)
126
+ return true
127
+ end
128
+
129
+ private
130
+
131
+ def change_dialect(dialect_name, location)
132
+ dialect = Dialect.for(dialect_name)
133
+ raise NoSuchLanguageException.new(dialect_name, location) if dialect.nil?
134
+
135
+ @dialect_name = dialect_name
136
+ @dialect = dialect
137
+ end
138
+
139
+ def match_title_line(token, token_type, keywords)
140
+ keyword = keywords.detect { |k| token.line.start_with_title_keyword?(k) }
141
+
142
+ return false unless keyword
143
+
144
+ title = token.line.get_rest_trimmed(keyword.length + ':'.length)
145
+ set_token_matched(token, token_type, title, keyword)
146
+ true
147
+ end
148
+
149
+ def set_token_matched(token, matched_type, text=nil, keyword=nil, indent=nil, items=[])
150
+ token.matched_type = matched_type
151
+ token.matched_text = text && text.chomp
152
+ token.matched_keyword = keyword
153
+ token.matched_indent = indent || (token.line && token.line.indent) || 0
154
+ token.matched_items = items
155
+ token.location[:column] = token.matched_indent + 1
156
+ token.matched_gherkin_dialect = @dialect_name
157
+ end
158
+
159
+ def unescape_docstring(text)
160
+ text.gsub("\\\"\\\"\\\"", "\"\"\"")
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,33 @@
1
+ require_relative 'token'
2
+ require_relative 'gherkin_line'
3
+
4
+ module Gherkin3
5
+ class TokenScanner
6
+
7
+ def initialize(source_or_path_or_io)
8
+ @line_number = 0
9
+ if String === source_or_path_or_io
10
+ if File.file?(source_or_path_or_io)
11
+ @io = File.open(source_or_path_or_io, 'r:BOM|UTF-8')
12
+ else
13
+ @io = StringIO.new(source_or_path_or_io)
14
+ end
15
+ else
16
+ @io = source_or_path_or_io
17
+ end
18
+ end
19
+
20
+ def read
21
+ location = {line: @line_number += 1}
22
+ if @io.nil? || line = @io.gets
23
+ gherkin_line = line ? GherkinLine.new(line, location[:line]) : nil
24
+ Token.new(gherkin_line, location)
25
+ else
26
+ @io.close unless @io.closed? # ARGF closes the last file after final gets
27
+ @io = nil
28
+ Token.new(nil, location)
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,68 @@
1
+ # With thanks to @myronmarston
2
+ # https://github.com/vcr/vcr/blob/master/spec/capture_warnings.rb
3
+
4
+ module CaptureWarnings
5
+ def report_warnings(&block)
6
+ current_dir = Dir.pwd
7
+ warnings, errors = capture_error(&block).partition { |line| line.include?('warning') }
8
+ project_warnings, other_warnings = warnings.uniq.partition { |line| line.include?(current_dir) }
9
+
10
+ if errors.any?
11
+ puts errors.join("\n")
12
+ end
13
+
14
+ if other_warnings.any?
15
+ puts "#{ other_warnings.count } non-gherkin3 warnings detected, set VIEW_OTHER_WARNINGS=true to see them."
16
+ print_warnings('other', other_warnings) if ENV['VIEW_OTHER_WARNINGS']
17
+ end
18
+
19
+ if project_warnings.any?
20
+ puts "#{ project_warnings.count } gherkin3 warnings detected"
21
+ print_warnings('gherkin3', project_warnings)
22
+ fail "Please remove all gherkin3 warnings."
23
+ end
24
+
25
+ ensure_system_exit_if_required
26
+ end
27
+
28
+ def capture_error(&block)
29
+ old_stderr = STDERR.clone
30
+ pipe_r, pipe_w = IO.pipe
31
+ pipe_r.sync = true
32
+ error = ""
33
+ reader = Thread.new do
34
+ begin
35
+ loop do
36
+ error << pipe_r.readpartial(1024)
37
+ end
38
+ rescue EOFError
39
+ end
40
+ end
41
+ STDERR.reopen(pipe_w)
42
+ block.call
43
+ ensure
44
+ capture_system_exit
45
+ STDERR.reopen(old_stderr)
46
+ pipe_w.close
47
+ reader.join
48
+ return error.split("\n")
49
+ end
50
+
51
+ def print_warnings(type, warnings)
52
+ puts
53
+ puts "-" * 30 + " #{type} warnings: " + "-" * 30
54
+ puts
55
+ puts warnings.join("\n")
56
+ puts
57
+ puts "-" * 75
58
+ puts
59
+ end
60
+
61
+ def ensure_system_exit_if_required
62
+ raise @system_exit if @system_exit
63
+ end
64
+
65
+ def capture_system_exit
66
+ @system_exit = $!
67
+ end
68
+ end
@@ -0,0 +1,10 @@
1
+ require 'simplecov'
2
+ formatters = [ SimpleCov::Formatter::HTMLFormatter ]
3
+
4
+ if ENV['TRAVIS']
5
+ require 'coveralls'
6
+ formatters << Coveralls::SimpleCov::Formatter
7
+ end
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[*formatters]
10
+ SimpleCov.start
@@ -0,0 +1,26 @@
1
+ require 'gherkin3/parser'
2
+ require 'gherkin3/token_scanner'
3
+ require 'gherkin3/token_matcher'
4
+ require 'gherkin3/ast_builder'
5
+ require 'rspec'
6
+
7
+ module Gherkin3
8
+ describe Parser do
9
+ it "parses a simple feature" do
10
+ parser = Parser.new
11
+ scanner = TokenScanner.new("Feature: test")
12
+ builder = AstBuilder.new
13
+ ast = parser.parse(scanner, builder, TokenMatcher.new)
14
+ expect(ast).to eq({
15
+ type: :Feature,
16
+ tags: [],
17
+ location: {line: 1, column: 1},
18
+ language: "en",
19
+ keyword: "Feature",
20
+ name: "test",
21
+ scenarioDefinitions: [],
22
+ comments: []
23
+ })
24
+ end
25
+ end
26
+ end
metadata ADDED
@@ -0,0 +1,130 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gherkin3
3
+ version: !ruby/object:Gem::Version
4
+ version: 3.0.0.alpha.1
5
+ platform: ruby
6
+ authors:
7
+ - Gáspár Nagy
8
+ - Aslak Hellesøy
9
+ - Steve Tooke
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2015-07-15 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: bundler
17
+ requirement: !ruby/object:Gem::Requirement
18
+ requirements:
19
+ - - "~>"
20
+ - !ruby/object:Gem::Version
21
+ version: '1.7'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - "~>"
27
+ - !ruby/object:Gem::Version
28
+ version: '1.7'
29
+ - !ruby/object:Gem::Dependency
30
+ name: rake
31
+ requirement: !ruby/object:Gem::Requirement
32
+ requirements:
33
+ - - "~>"
34
+ - !ruby/object:Gem::Version
35
+ version: '10.4'
36
+ type: :development
37
+ prerelease: false
38
+ version_requirements: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - "~>"
41
+ - !ruby/object:Gem::Version
42
+ version: '10.4'
43
+ - !ruby/object:Gem::Dependency
44
+ name: rspec
45
+ requirement: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '3.3'
50
+ type: :development
51
+ prerelease: false
52
+ version_requirements: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - "~>"
55
+ - !ruby/object:Gem::Version
56
+ version: '3.3'
57
+ - !ruby/object:Gem::Dependency
58
+ name: coveralls
59
+ requirement: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.8'
64
+ type: :development
65
+ prerelease: false
66
+ version_requirements: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - "~>"
69
+ - !ruby/object:Gem::Version
70
+ version: '0.8'
71
+ description: Gherkin parser
72
+ email: cukes@googlegroups.com
73
+ executables: []
74
+ extensions: []
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".travis.yml"
78
+ - CONTRIBUTING.md
79
+ - Gemfile
80
+ - LICENSE
81
+ - Makefile
82
+ - README.md
83
+ - Rakefile
84
+ - bin/gherkin-generate-ast
85
+ - bin/gherkin-generate-tokens
86
+ - gherkin-ruby.razor
87
+ - gherkin3.gemspec
88
+ - lib/gherkin3/ast_builder.rb
89
+ - lib/gherkin3/ast_node.rb
90
+ - lib/gherkin3/dialect.rb
91
+ - lib/gherkin3/errors.rb
92
+ - lib/gherkin3/gherkin-languages.json
93
+ - lib/gherkin3/gherkin_line.rb
94
+ - lib/gherkin3/parser.rb
95
+ - lib/gherkin3/token.rb
96
+ - lib/gherkin3/token_formatter_builder.rb
97
+ - lib/gherkin3/token_matcher.rb
98
+ - lib/gherkin3/token_scanner.rb
99
+ - spec/capture_warnings.rb
100
+ - spec/coverage.rb
101
+ - spec/gherkin3/parser_spec.rb
102
+ homepage: https://github.com/cucumber/gherkin3
103
+ licenses:
104
+ - MIT
105
+ metadata: {}
106
+ post_install_message:
107
+ rdoc_options:
108
+ - "--charset=UTF-8"
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: 1.9.3
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">"
119
+ - !ruby/object:Gem::Version
120
+ version: 1.3.1
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.4.5
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: gherkin-3.0.0.alpha.1
127
+ test_files:
128
+ - spec/capture_warnings.rb
129
+ - spec/coverage.rb
130
+ - spec/gherkin3/parser_spec.rb