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