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.
- checksums.yaml +7 -0
- data/.travis.yml +1 -0
- data/CONTRIBUTING.md +16 -0
- data/Gemfile +2 -0
- data/LICENSE +21 -0
- data/Makefile +63 -0
- data/README.md +3 -0
- data/Rakefile +24 -0
- data/bin/gherkin-generate-ast +24 -0
- data/bin/gherkin-generate-tokens +14 -0
- data/gherkin-ruby.razor +211 -0
- data/gherkin3.gemspec +26 -0
- data/lib/gherkin3/ast_builder.rb +239 -0
- data/lib/gherkin3/ast_node.rb +30 -0
- data/lib/gherkin3/dialect.rb +58 -0
- data/lib/gherkin3/errors.rb +45 -0
- data/lib/gherkin3/gherkin-languages.json +2835 -0
- data/lib/gherkin3/gherkin_line.rb +66 -0
- data/lib/gherkin3/parser.rb +1903 -0
- data/lib/gherkin3/token.rb +18 -0
- data/lib/gherkin3/token_formatter_builder.rb +35 -0
- data/lib/gherkin3/token_matcher.rb +163 -0
- data/lib/gherkin3/token_scanner.rb +33 -0
- data/spec/capture_warnings.rb +68 -0
- data/spec/coverage.rb +10 -0
- data/spec/gherkin3/parser_spec.rb +26 -0
- metadata +130 -0
@@ -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
|
data/spec/coverage.rb
ADDED
@@ -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
|