gherkin 1.0.30-universal-dotnet
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.
- data/.gitattributes +2 -0
- data/.gitignore +9 -0
- data/.mailmap +2 -0
- data/History.txt +187 -0
- data/LICENSE +20 -0
- data/README.rdoc +59 -0
- data/Rakefile +58 -0
- data/VERSION.yml +5 -0
- data/bin/gherkin +5 -0
- data/cucumber.yml +3 -0
- data/features/escaped_pipes.feature +8 -0
- data/features/feature_parser.feature +226 -0
- data/features/native_lexer.feature +19 -0
- data/features/parser_with_native_lexer.feature +205 -0
- data/features/pretty_printer.feature +14 -0
- data/features/step_definitions/eyeball_steps.rb +3 -0
- data/features/step_definitions/gherkin_steps.rb +30 -0
- data/features/step_definitions/pretty_formatter_steps.rb +55 -0
- data/features/steps_parser.feature +46 -0
- data/features/support/env.rb +33 -0
- data/ikvm/.gitignore +3 -0
- data/java/.gitignore +2 -0
- data/java/src/main/java/gherkin/lexer/.gitignore +1 -0
- data/java/src/main/resources/gherkin/.gitignore +1 -0
- data/lib/.gitignore +4 -0
- data/lib/gherkin.rb +2 -0
- data/lib/gherkin/c_lexer.rb +17 -0
- data/lib/gherkin/cli/main.rb +33 -0
- data/lib/gherkin/formatter/argument.rb +27 -0
- data/lib/gherkin/formatter/colors.rb +119 -0
- data/lib/gherkin/formatter/escaping.rb +15 -0
- data/lib/gherkin/formatter/monochrome_format.rb +9 -0
- data/lib/gherkin/formatter/pretty_formatter.rb +168 -0
- data/lib/gherkin/i18n.rb +176 -0
- data/lib/gherkin/i18n.yml +588 -0
- data/lib/gherkin/i18n_lexer.rb +38 -0
- data/lib/gherkin/native.rb +7 -0
- data/lib/gherkin/native/ikvm.rb +55 -0
- data/lib/gherkin/native/java.rb +47 -0
- data/lib/gherkin/native/null.rb +9 -0
- data/lib/gherkin/parser/event.rb +45 -0
- data/lib/gherkin/parser/filter_listener.rb +199 -0
- data/lib/gherkin/parser/meta.txt +5 -0
- data/lib/gherkin/parser/parser.rb +142 -0
- data/lib/gherkin/parser/root.txt +11 -0
- data/lib/gherkin/parser/steps.txt +4 -0
- data/lib/gherkin/parser/tag_expression.rb +50 -0
- data/lib/gherkin/rb_lexer.rb +8 -0
- data/lib/gherkin/rb_lexer/.gitignore +1 -0
- data/lib/gherkin/rb_lexer/README.rdoc +8 -0
- data/lib/gherkin/rubify.rb +18 -0
- data/lib/gherkin/tools.rb +8 -0
- data/lib/gherkin/tools/files.rb +35 -0
- data/lib/gherkin/tools/reformat.rb +19 -0
- data/lib/gherkin/tools/stats.rb +21 -0
- data/lib/gherkin/tools/stats_listener.rb +57 -0
- data/ragel/i18n/.gitignore +1 -0
- data/ragel/lexer.c.rl.erb +425 -0
- data/ragel/lexer.java.rl.erb +216 -0
- data/ragel/lexer.rb.rl.erb +173 -0
- data/ragel/lexer_common.rl.erb +50 -0
- data/spec/gherkin/c_lexer_spec.rb +21 -0
- data/spec/gherkin/csharp_lexer_spec.rb +20 -0
- data/spec/gherkin/fixtures/1.feature +8 -0
- data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
- data/spec/gherkin/fixtures/complex.feature +45 -0
- data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
- data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
- data/spec/gherkin/fixtures/i18n_no.feature +7 -0
- data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
- data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
- data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
- data/spec/gherkin/fixtures/with_bom.feature +3 -0
- data/spec/gherkin/formatter/argument_spec.rb +28 -0
- data/spec/gherkin/formatter/colors_spec.rb +19 -0
- data/spec/gherkin/formatter/pretty_formatter_spec.rb +162 -0
- data/spec/gherkin/formatter/spaces.feature +9 -0
- data/spec/gherkin/formatter/tabs.feature +9 -0
- data/spec/gherkin/i18n_lexer_spec.rb +26 -0
- data/spec/gherkin/i18n_spec.rb +144 -0
- data/spec/gherkin/java_lexer_spec.rb +21 -0
- data/spec/gherkin/parser/filter_listener_spec.rb +390 -0
- data/spec/gherkin/parser/parser_spec.rb +50 -0
- data/spec/gherkin/parser/tag_expression_spec.rb +116 -0
- data/spec/gherkin/rb_lexer_spec.rb +19 -0
- data/spec/gherkin/sexp_recorder.rb +32 -0
- data/spec/gherkin/shared/lexer_spec.rb +550 -0
- data/spec/gherkin/shared/py_string_spec.rb +150 -0
- data/spec/gherkin/shared/row_spec.rb +104 -0
- data/spec/gherkin/shared/tags_spec.rb +50 -0
- data/spec/spec_helper.rb +87 -0
- data/tasks/bench.rake +188 -0
- data/tasks/bench/feature_builder.rb +49 -0
- data/tasks/bench/generated/.gitignore +1 -0
- data/tasks/bench/null_listener.rb +4 -0
- data/tasks/compile.rake +89 -0
- data/tasks/cucumber.rake +26 -0
- data/tasks/gems.rake +45 -0
- data/tasks/ikvm.rake +47 -0
- data/tasks/ragel_task.rb +70 -0
- data/tasks/rdoc.rake +12 -0
- data/tasks/release.rake +26 -0
- data/tasks/rspec.rake +15 -0
- metadata +257 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
begin
|
2
|
+
require 'trollop'
|
3
|
+
rescue LoadError
|
4
|
+
require 'rubygems'
|
5
|
+
require 'trollop'
|
6
|
+
end
|
7
|
+
require 'gherkin/tools'
|
8
|
+
|
9
|
+
module Gherkin
|
10
|
+
module Cli
|
11
|
+
class Main
|
12
|
+
def self.run(args)
|
13
|
+
Trollop::options(args) do
|
14
|
+
banner "Super fast gherkin parser"
|
15
|
+
stop_on Tools::SUB_COMMANDS
|
16
|
+
end
|
17
|
+
|
18
|
+
cmd_name = args.shift
|
19
|
+
die("Missing command") if cmd_name.nil?
|
20
|
+
begin
|
21
|
+
cmd = Tools.const_get(cmd_name.capitalize.to_sym).new(args)
|
22
|
+
cmd.run
|
23
|
+
rescue => e
|
24
|
+
Trollop::die(e.message + "\nCommand: #{cmd_name}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.die(msg)
|
29
|
+
Trollop::die("#{msg}\nusage: gherkin COMMAND [ARGS]\nAvailable commands: #{Tools::SUB_COMMANDS.join(' ')}")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'gherkin/native'
|
2
|
+
|
3
|
+
module Gherkin
|
4
|
+
module Formatter
|
5
|
+
class Argument
|
6
|
+
native_impl('gherkin')
|
7
|
+
attr_reader :byte_offset, :val
|
8
|
+
|
9
|
+
def initialize(byte_offset, val)
|
10
|
+
@byte_offset, @val = byte_offset, val
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.format(string, argument_format, arguments)
|
14
|
+
s = string.dup
|
15
|
+
offset = past_offset = 0
|
16
|
+
arguments.each do |arg|
|
17
|
+
next if arg.byte_offset.nil? || arg.byte_offset < past_offset
|
18
|
+
replacement = argument_format.format_argument(arg.val)
|
19
|
+
s[arg.byte_offset + offset, arg.val.length] = replacement
|
20
|
+
offset += replacement.unpack("U*").length - arg.val.unpack("U*").length
|
21
|
+
past_offset = arg.byte_offset + arg.val.length
|
22
|
+
end
|
23
|
+
s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module Gherkin
|
4
|
+
module Formatter
|
5
|
+
# Defines aliases for coloured output. You don't invoke any methods from this
|
6
|
+
# module directly, but you can change the output colours by defining
|
7
|
+
# a <tt>GHERKIN_COLORS</tt> variable in your shell, very much like how you can
|
8
|
+
# tweak the familiar POSIX command <tt>ls</tt> with
|
9
|
+
# <a href="http://mipsisrisc.com/rambling/2008/06/27/lscolorsls_colors-now-with-linux-support/">$LSCOLORS/$LS_COLORS</a>
|
10
|
+
#
|
11
|
+
# The colours that you can change are:
|
12
|
+
#
|
13
|
+
# * <tt>undefined</tt> - defaults to <tt>yellow</tt>
|
14
|
+
# * <tt>pending</tt> - defaults to <tt>yellow</tt>
|
15
|
+
# * <tt>pending_param</tt> - defaults to <tt>yellow,bold</tt>
|
16
|
+
# * <tt>failed</tt> - defaults to <tt>red</tt>
|
17
|
+
# * <tt>failed_param</tt> - defaults to <tt>red,bold</tt>
|
18
|
+
# * <tt>passed</tt> - defaults to <tt>green</tt>
|
19
|
+
# * <tt>passed_param</tt> - defaults to <tt>green,bold</tt>
|
20
|
+
# * <tt>outline</tt> - defaults to <tt>cyan</tt>
|
21
|
+
# * <tt>outline_param</tt> - defaults to <tt>cyan,bold</tt>
|
22
|
+
# * <tt>skipped</tt> - defaults to <tt>cyan</tt>
|
23
|
+
# * <tt>skipped_param</tt> - defaults to <tt>cyan,bold</tt>
|
24
|
+
# * <tt>comment</tt> - defaults to <tt>grey</tt>
|
25
|
+
# * <tt>tag</tt> - defaults to <tt>cyan</tt>
|
26
|
+
#
|
27
|
+
# For instance, if your shell has a black background and a green font (like the
|
28
|
+
# "Homebrew" settings for OS X' Terminal.app), you may want to override passed
|
29
|
+
# steps to be white instead of green. Examples:
|
30
|
+
#
|
31
|
+
# export GHERKIN_COLORS="passed=white"
|
32
|
+
# export GHERKIN_COLORS="passed=white,bold:passed_param=white,bold,underline"
|
33
|
+
#
|
34
|
+
# (If you're on Windows, use SET instead of export).
|
35
|
+
# To see what colours and effects are available, just run this in your shell:
|
36
|
+
#
|
37
|
+
# ruby -e "require 'rubygems'; require 'term/ansicolor'; puts Term::ANSIColor.attributes"
|
38
|
+
#
|
39
|
+
# Although not listed, you can also use <tt>grey</tt>
|
40
|
+
module Colors
|
41
|
+
include Term::ANSIColor
|
42
|
+
|
43
|
+
ALIASES = Hash.new do |h,k|
|
44
|
+
if k.to_s =~ /(.*)_param/
|
45
|
+
h[$1] + ',bold'
|
46
|
+
end
|
47
|
+
end.merge({
|
48
|
+
'undefined' => 'yellow',
|
49
|
+
'pending' => 'yellow',
|
50
|
+
'failed' => 'red',
|
51
|
+
'passed' => 'green',
|
52
|
+
'outline' => 'cyan',
|
53
|
+
'skipped' => 'cyan',
|
54
|
+
'comments' => 'grey',
|
55
|
+
'tag' => 'cyan'
|
56
|
+
})
|
57
|
+
|
58
|
+
if ENV['GHERKIN_COLORS'] # Example: export GHERKIN_COLORS="passed=red:failed=yellow"
|
59
|
+
ENV['GHERKIN_COLORS'].split(':').each do |pair|
|
60
|
+
a = pair.split('=')
|
61
|
+
ALIASES[a[0]] = a[1]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
ALIASES.each do |method, color|
|
66
|
+
unless method =~ /.*_param/
|
67
|
+
code = <<-EOF
|
68
|
+
def #{method}(string=nil, monochrome=false, &proc)
|
69
|
+
return string if monochrome
|
70
|
+
#{ALIASES[method].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method].split(",").length}
|
71
|
+
end
|
72
|
+
# This resets the colour to the non-param colour
|
73
|
+
def #{method}_param(string=nil, monochrome=false, &proc)
|
74
|
+
return string if monochrome
|
75
|
+
#{ALIASES[method+'_param'].split(",").join("(") + "(string, &proc" + ")" * ALIASES[method+'_param'].split(",").length} + #{ALIASES[method].split(",").join(' + ')}
|
76
|
+
end
|
77
|
+
EOF
|
78
|
+
eval(code)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.define_grey #:nodoc:
|
83
|
+
begin
|
84
|
+
gem 'genki-ruby-terminfo'
|
85
|
+
require 'terminfo'
|
86
|
+
case TermInfo.default_object.tigetnum("colors")
|
87
|
+
when 0
|
88
|
+
raise "Your terminal doesn't support colours"
|
89
|
+
when 1
|
90
|
+
::Term::ANSIColor.coloring = false
|
91
|
+
alias grey white
|
92
|
+
when 2..8
|
93
|
+
alias grey white
|
94
|
+
else
|
95
|
+
define_real_grey
|
96
|
+
end
|
97
|
+
rescue Exception => e
|
98
|
+
if e.class.name == 'TermInfo::TermInfoError'
|
99
|
+
STDERR.puts "*** WARNING ***"
|
100
|
+
STDERR.puts "You have the genki-ruby-terminfo gem installed, but you haven't set your TERM variable."
|
101
|
+
STDERR.puts "Try setting it to TERM=xterm-256color to get grey colour in output"
|
102
|
+
STDERR.puts "\n"
|
103
|
+
alias grey white
|
104
|
+
else
|
105
|
+
define_real_grey
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def self.define_real_grey #:nodoc:
|
111
|
+
def grey(m) #:nodoc:
|
112
|
+
"\e[90m#{m}\e[0m"
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
define_grey
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Gherkin
|
2
|
+
module Formatter
|
3
|
+
module Escaping
|
4
|
+
# Escapes a pipes and backslashes:
|
5
|
+
#
|
6
|
+
# * | becomes \|
|
7
|
+
# * \ becomes \\
|
8
|
+
#
|
9
|
+
# This is used in the pretty formatter.
|
10
|
+
def escape_cell(s)
|
11
|
+
s.gsub(/\|/, "\\|").gsub(/\\(?!\|)/, "\\\\\\\\")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'gherkin/formatter/colors'
|
3
|
+
require 'gherkin/formatter/monochrome_format'
|
4
|
+
require 'gherkin/formatter/argument'
|
5
|
+
require 'gherkin/formatter/escaping'
|
6
|
+
require 'gherkin/native'
|
7
|
+
|
8
|
+
module Gherkin
|
9
|
+
module Formatter
|
10
|
+
class PrettyFormatter
|
11
|
+
native_impl('gherkin')
|
12
|
+
|
13
|
+
include Colors
|
14
|
+
include Escaping
|
15
|
+
|
16
|
+
def initialize(io, monochrome=false)
|
17
|
+
@io = io
|
18
|
+
@monochrome = monochrome
|
19
|
+
@format = MonochromeFormat.new #@monochrome ? MonochromeFormat.new : AnsiColorFormat.new
|
20
|
+
@tags = nil
|
21
|
+
@comments = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def tag(name, line)
|
25
|
+
@tags ||= []
|
26
|
+
@tags << name
|
27
|
+
end
|
28
|
+
|
29
|
+
def comment(content, line)
|
30
|
+
@comments ||= []
|
31
|
+
@comments << content
|
32
|
+
end
|
33
|
+
|
34
|
+
def feature(keyword, name, line)
|
35
|
+
@io.puts "#{grab_comments!('')}#{grab_tags!('')}#{keyword}: #{indent(name, ' ')}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def background(keyword, name, line)
|
39
|
+
@io.puts "\n#{grab_comments!(' ')} #{keyword}: #{indent(name, ' ')}"
|
40
|
+
end
|
41
|
+
|
42
|
+
def scenario(keyword, name, line, location=nil)
|
43
|
+
flush_table
|
44
|
+
@io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}#{indented_scenario_location!(keyword, name, location)}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def scenario_outline(keyword, name, line)
|
48
|
+
flush_table
|
49
|
+
@io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def examples(keyword, name, line)
|
53
|
+
flush_table
|
54
|
+
@io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
|
55
|
+
end
|
56
|
+
|
57
|
+
def step(keyword, name, line, status=nil, exception=nil, arguments=nil, location=nil)
|
58
|
+
flush_table
|
59
|
+
status_param = "#{status}_param" if status
|
60
|
+
name = Gherkin::Formatter::Argument.format(name, @format, (arguments || []))
|
61
|
+
#{|arg| status_param ? self.__send__(status_param, arg, @monochrome) : arg} if arguments
|
62
|
+
|
63
|
+
step = "#{keyword}#{indent(name, ' ')}"
|
64
|
+
step = self.__send__(status, step, @monochrome) if status
|
65
|
+
|
66
|
+
@io.puts("#{grab_comments!(' ')} #{step}#{indented_step_location!(location)}")
|
67
|
+
end
|
68
|
+
|
69
|
+
def row(row, line)
|
70
|
+
@rows ||= []
|
71
|
+
@rows << row.map{|cell| escape_cell(cell)}
|
72
|
+
end
|
73
|
+
|
74
|
+
def py_string(string, line)
|
75
|
+
@io.puts " \"\"\"\n" + string.gsub(START, ' ').gsub(/"""/,'\"\"\"') + "\n \"\"\""
|
76
|
+
end
|
77
|
+
|
78
|
+
def syntax_error(state, event, legal_events, line)
|
79
|
+
raise "SYNTAX ERROR"
|
80
|
+
end
|
81
|
+
|
82
|
+
def eof
|
83
|
+
flush_table
|
84
|
+
end
|
85
|
+
|
86
|
+
# This method can be invoked before a #scenario, to ensure location arguments are aligned
|
87
|
+
def steps(steps)
|
88
|
+
@step_lengths = steps.map {|keyword, name| (keyword+name).unpack("U*").length}
|
89
|
+
@max_step_length = @step_lengths.max
|
90
|
+
@step_index = -1
|
91
|
+
end
|
92
|
+
|
93
|
+
def exception(exception)
|
94
|
+
exception_text = "#{exception.message} (#{exception.class})\n#{(exception.backtrace || []).join("\n")}".gsub(/^/, ' ')
|
95
|
+
@io.puts(failed(exception_text, @monochrome))
|
96
|
+
end
|
97
|
+
|
98
|
+
def flush_table(exception=nil, statuses=nil)
|
99
|
+
return if @rows.nil?
|
100
|
+
cell_lengths = @rows.map { |col| col.map { |cell| cell.unpack("U*").length }}
|
101
|
+
max_lengths = cell_lengths.transpose.map { |col_lengths| col_lengths.max }.flatten
|
102
|
+
|
103
|
+
@rows.each_with_index do |row, i|
|
104
|
+
j = -1
|
105
|
+
@io.puts ' | ' + row.zip(max_lengths).map { |cell, max_length|
|
106
|
+
j += 1
|
107
|
+
color(cell, statuses, j) + ' ' * (max_length - cell_lengths[i][j])
|
108
|
+
}.join(' | ') + ' |'
|
109
|
+
exception(exception) if exception
|
110
|
+
end
|
111
|
+
@rows = nil
|
112
|
+
end
|
113
|
+
|
114
|
+
private
|
115
|
+
|
116
|
+
def color(cell, statuses, col)
|
117
|
+
if statuses
|
118
|
+
self.__send__(statuses[col], cell, @monochrome) + (@monochrome ? '' : reset)
|
119
|
+
else
|
120
|
+
cell
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
if(RUBY_VERSION =~ /^1\.9/)
|
125
|
+
START = /#{"^".encode('UTF-8')}/
|
126
|
+
NL = Regexp.new("\n".encode('UTF-8'))
|
127
|
+
else
|
128
|
+
START = /^/
|
129
|
+
NL = /\n/n
|
130
|
+
end
|
131
|
+
|
132
|
+
def indent(string, indentation)
|
133
|
+
indent = ""
|
134
|
+
string.split(NL).map do |l|
|
135
|
+
s = "#{indent}#{l}"
|
136
|
+
indent = indentation
|
137
|
+
s
|
138
|
+
end.join("\n")
|
139
|
+
end
|
140
|
+
|
141
|
+
def grab_tags!(indent)
|
142
|
+
tags = @tags ? indent + @tags.join(' ') + "\n" : ''
|
143
|
+
@tags = nil
|
144
|
+
tags
|
145
|
+
end
|
146
|
+
|
147
|
+
def grab_comments!(indent)
|
148
|
+
comments = @comments ? indent + @comments.join("\n#{indent}") + "\n" : ''
|
149
|
+
@comments = nil
|
150
|
+
comments
|
151
|
+
end
|
152
|
+
|
153
|
+
def indented_scenario_location!(keyword, name, location)
|
154
|
+
return "" if location.nil?
|
155
|
+
l = (keyword+name).unpack("U*").length
|
156
|
+
@max_step_length = [@max_step_length, l].max
|
157
|
+
indent = @max_step_length - l
|
158
|
+
' ' * indent + ' ' + comments("# #{location}", @monochrome)
|
159
|
+
end
|
160
|
+
|
161
|
+
def indented_step_location!(location)
|
162
|
+
return "" if location.nil?
|
163
|
+
indent = @max_step_length - @step_lengths[@step_index+=1]
|
164
|
+
' ' * indent + ' ' + comments("# #{location}", @monochrome)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
data/lib/gherkin/i18n.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'gherkin/rubify'
|
3
|
+
require 'gherkin/native'
|
4
|
+
|
5
|
+
module Gherkin
|
6
|
+
class I18n
|
7
|
+
native_impl('gherkin') unless defined?(BYPASS_NATIVE_IMPL)
|
8
|
+
|
9
|
+
FEATURE_ELEMENT_KEYS = %w{feature background scenario scenario_outline examples}
|
10
|
+
STEP_KEYWORD_KEYS = %w{given when then and but}
|
11
|
+
KEYWORD_KEYS = FEATURE_ELEMENT_KEYS + STEP_KEYWORD_KEYS
|
12
|
+
LANGUAGES = YAML.load_file(File.dirname(__FILE__) + '/i18n.yml')
|
13
|
+
|
14
|
+
class << self
|
15
|
+
include Rubify
|
16
|
+
|
17
|
+
# Used by code generators for other lexer tools like pygments lexer and textmate bundle
|
18
|
+
def all
|
19
|
+
LANGUAGES.keys.sort.map{|iso_code| get(iso_code)}
|
20
|
+
end
|
21
|
+
|
22
|
+
def get(iso_code)
|
23
|
+
languages[iso_code] ||= new(iso_code)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns all keyword translations and aliases of +keywords+, escaped and joined with <tt>|</tt>.
|
27
|
+
# This method is convenient for editor support and syntax highlighting engines for Gherkin, where
|
28
|
+
# there is typically a code generation tool to generate regular expressions for recognising the
|
29
|
+
# various I18n translations of Gherkin's keywords.
|
30
|
+
#
|
31
|
+
# The +keywords+ arguments can be one of <tt>:feature</tt>, <tt>:background</tt>, <tt>:scenario</tt>,
|
32
|
+
# <tt>:scenario_outline</tt>, <tt>:examples</tt>, <tt>:step</tt>.
|
33
|
+
def keyword_regexp(*keywords)
|
34
|
+
unique_keywords = all.map do |i18n|
|
35
|
+
keywords.map do |keyword|
|
36
|
+
if keyword.to_s == 'step'
|
37
|
+
i18n.step_keywords
|
38
|
+
else
|
39
|
+
i18n.keywords(keyword)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
unique_keywords.flatten.compact.sort.reverse.uniq.join('|').gsub(/\*/, '\*')
|
45
|
+
end
|
46
|
+
|
47
|
+
def code_keywords
|
48
|
+
rubify(all.map{|i18n| i18n.code_keywords}).flatten.uniq.sort
|
49
|
+
end
|
50
|
+
|
51
|
+
def code_keyword_for(gherkin_keyword)
|
52
|
+
gherkin_keyword.gsub(/[\s',]/, '').strip
|
53
|
+
end
|
54
|
+
|
55
|
+
def language_table
|
56
|
+
require 'stringio'
|
57
|
+
require 'gherkin/formatter/pretty_formatter'
|
58
|
+
io = defined?(JRUBY_VERSION) ? Java.java.io.StringWriter.new : StringIO.new
|
59
|
+
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
60
|
+
all.each{|i18n| pf.row([i18n.iso_code, i18n.keywords('name')[0], i18n.keywords('native')[0]], 0)}
|
61
|
+
pf.flush_table
|
62
|
+
if defined?(JRUBY_VERSION)
|
63
|
+
io.getBuffer.toString
|
64
|
+
else
|
65
|
+
io.rewind
|
66
|
+
io.read
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def unicode_escape(word, prefix="\\u")
|
71
|
+
word = word.unpack("U*").map do |c|
|
72
|
+
if c > 127 || c == 32
|
73
|
+
"#{prefix}%04x" % c
|
74
|
+
else
|
75
|
+
c.chr
|
76
|
+
end
|
77
|
+
end.join
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def languages
|
83
|
+
@languages ||= {}
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
attr_reader :iso_code
|
88
|
+
|
89
|
+
def initialize(iso_code)
|
90
|
+
@iso_code = iso_code
|
91
|
+
@keywords = LANGUAGES[iso_code]
|
92
|
+
raise "Language not supported: #{iso_code.inspect}" if @iso_code.nil?
|
93
|
+
@keywords['grammar_name'] = @keywords['name'].gsub(/\s/, '')
|
94
|
+
end
|
95
|
+
|
96
|
+
def lexer(listener, force_ruby=false)
|
97
|
+
begin
|
98
|
+
if force_ruby
|
99
|
+
rb(listener)
|
100
|
+
else
|
101
|
+
begin
|
102
|
+
c(listener)
|
103
|
+
rescue NameError, LoadError => e
|
104
|
+
warn("WARNING: #{e.message}. Reverting to Ruby lexer.")
|
105
|
+
rb(listener)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
rescue LoadError => e
|
109
|
+
raise I18nLexerNotFound, "No lexer was found for #{i18n_language_name} (#{e.message}). Supported languages are listed in gherkin/i18n.yml."
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def c(listener)
|
114
|
+
require 'gherkin/c_lexer'
|
115
|
+
CLexer[underscored_iso_code].new(listener)
|
116
|
+
end
|
117
|
+
|
118
|
+
def rb(listener)
|
119
|
+
require 'gherkin/rb_lexer'
|
120
|
+
RbLexer[underscored_iso_code].new(listener)
|
121
|
+
end
|
122
|
+
|
123
|
+
def underscored_iso_code
|
124
|
+
@iso_code.gsub(/[\s-]/, '_').downcase
|
125
|
+
end
|
126
|
+
|
127
|
+
# Keywords that can be used in Gherkin source
|
128
|
+
def step_keywords
|
129
|
+
STEP_KEYWORD_KEYS.map{|iso_code| keywords(iso_code)}.flatten.uniq
|
130
|
+
end
|
131
|
+
|
132
|
+
# Keywords that can be used in code
|
133
|
+
def code_keywords
|
134
|
+
result = step_keywords.map{|keyword| self.class.code_keyword_for(keyword)}
|
135
|
+
result.delete('*')
|
136
|
+
result
|
137
|
+
end
|
138
|
+
|
139
|
+
def keywords(iso_code)
|
140
|
+
iso_code = iso_code.to_s
|
141
|
+
raise "No #{iso_code.inspect} in #{@keywords.inspect}" if @keywords[iso_code].nil?
|
142
|
+
@keywords[iso_code].split('|').map{|keyword| real_keyword(iso_code, keyword)}
|
143
|
+
end
|
144
|
+
|
145
|
+
def keyword_table
|
146
|
+
require 'stringio'
|
147
|
+
require 'gherkin/formatter/pretty_formatter'
|
148
|
+
io = StringIO.new
|
149
|
+
pf = Gherkin::Formatter::PrettyFormatter.new(io, true)
|
150
|
+
|
151
|
+
KEYWORD_KEYS.each do |key|
|
152
|
+
pf.row([key, keywords(key).map{|keyword| %{"#{keyword}"}}.join(', ')], 0)
|
153
|
+
end
|
154
|
+
STEP_KEYWORD_KEYS.each do |key|
|
155
|
+
code_keywords = keywords(key).reject{|keyword| keyword == '* '}.map do |keyword|
|
156
|
+
%{"#{self.class.code_keyword_for(keyword)}"}
|
157
|
+
end.join(', ')
|
158
|
+
pf.row(["#{key} (code)", code_keywords], 0)
|
159
|
+
end
|
160
|
+
|
161
|
+
pf.flush_table
|
162
|
+
io.rewind
|
163
|
+
io.read
|
164
|
+
end
|
165
|
+
|
166
|
+
private
|
167
|
+
|
168
|
+
def real_keyword(iso_code, keyword)
|
169
|
+
if(STEP_KEYWORD_KEYS.index(iso_code))
|
170
|
+
(keyword + ' ').sub(/< $/, '')
|
171
|
+
else
|
172
|
+
keyword
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|