gherkin3 3.0.0.alpha.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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