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.
Files changed (104) hide show
  1. data/.gitattributes +2 -0
  2. data/.gitignore +9 -0
  3. data/.mailmap +2 -0
  4. data/History.txt +187 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +59 -0
  7. data/Rakefile +58 -0
  8. data/VERSION.yml +5 -0
  9. data/bin/gherkin +5 -0
  10. data/cucumber.yml +3 -0
  11. data/features/escaped_pipes.feature +8 -0
  12. data/features/feature_parser.feature +226 -0
  13. data/features/native_lexer.feature +19 -0
  14. data/features/parser_with_native_lexer.feature +205 -0
  15. data/features/pretty_printer.feature +14 -0
  16. data/features/step_definitions/eyeball_steps.rb +3 -0
  17. data/features/step_definitions/gherkin_steps.rb +30 -0
  18. data/features/step_definitions/pretty_formatter_steps.rb +55 -0
  19. data/features/steps_parser.feature +46 -0
  20. data/features/support/env.rb +33 -0
  21. data/ikvm/.gitignore +3 -0
  22. data/java/.gitignore +2 -0
  23. data/java/src/main/java/gherkin/lexer/.gitignore +1 -0
  24. data/java/src/main/resources/gherkin/.gitignore +1 -0
  25. data/lib/.gitignore +4 -0
  26. data/lib/gherkin.rb +2 -0
  27. data/lib/gherkin/c_lexer.rb +17 -0
  28. data/lib/gherkin/cli/main.rb +33 -0
  29. data/lib/gherkin/formatter/argument.rb +27 -0
  30. data/lib/gherkin/formatter/colors.rb +119 -0
  31. data/lib/gherkin/formatter/escaping.rb +15 -0
  32. data/lib/gherkin/formatter/monochrome_format.rb +9 -0
  33. data/lib/gherkin/formatter/pretty_formatter.rb +168 -0
  34. data/lib/gherkin/i18n.rb +176 -0
  35. data/lib/gherkin/i18n.yml +588 -0
  36. data/lib/gherkin/i18n_lexer.rb +38 -0
  37. data/lib/gherkin/native.rb +7 -0
  38. data/lib/gherkin/native/ikvm.rb +55 -0
  39. data/lib/gherkin/native/java.rb +47 -0
  40. data/lib/gherkin/native/null.rb +9 -0
  41. data/lib/gherkin/parser/event.rb +45 -0
  42. data/lib/gherkin/parser/filter_listener.rb +199 -0
  43. data/lib/gherkin/parser/meta.txt +5 -0
  44. data/lib/gherkin/parser/parser.rb +142 -0
  45. data/lib/gherkin/parser/root.txt +11 -0
  46. data/lib/gherkin/parser/steps.txt +4 -0
  47. data/lib/gherkin/parser/tag_expression.rb +50 -0
  48. data/lib/gherkin/rb_lexer.rb +8 -0
  49. data/lib/gherkin/rb_lexer/.gitignore +1 -0
  50. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  51. data/lib/gherkin/rubify.rb +18 -0
  52. data/lib/gherkin/tools.rb +8 -0
  53. data/lib/gherkin/tools/files.rb +35 -0
  54. data/lib/gherkin/tools/reformat.rb +19 -0
  55. data/lib/gherkin/tools/stats.rb +21 -0
  56. data/lib/gherkin/tools/stats_listener.rb +57 -0
  57. data/ragel/i18n/.gitignore +1 -0
  58. data/ragel/lexer.c.rl.erb +425 -0
  59. data/ragel/lexer.java.rl.erb +216 -0
  60. data/ragel/lexer.rb.rl.erb +173 -0
  61. data/ragel/lexer_common.rl.erb +50 -0
  62. data/spec/gherkin/c_lexer_spec.rb +21 -0
  63. data/spec/gherkin/csharp_lexer_spec.rb +20 -0
  64. data/spec/gherkin/fixtures/1.feature +8 -0
  65. data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
  66. data/spec/gherkin/fixtures/complex.feature +45 -0
  67. data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
  68. data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
  69. data/spec/gherkin/fixtures/i18n_no.feature +7 -0
  70. data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
  71. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  72. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  73. data/spec/gherkin/fixtures/with_bom.feature +3 -0
  74. data/spec/gherkin/formatter/argument_spec.rb +28 -0
  75. data/spec/gherkin/formatter/colors_spec.rb +19 -0
  76. data/spec/gherkin/formatter/pretty_formatter_spec.rb +162 -0
  77. data/spec/gherkin/formatter/spaces.feature +9 -0
  78. data/spec/gherkin/formatter/tabs.feature +9 -0
  79. data/spec/gherkin/i18n_lexer_spec.rb +26 -0
  80. data/spec/gherkin/i18n_spec.rb +144 -0
  81. data/spec/gherkin/java_lexer_spec.rb +21 -0
  82. data/spec/gherkin/parser/filter_listener_spec.rb +390 -0
  83. data/spec/gherkin/parser/parser_spec.rb +50 -0
  84. data/spec/gherkin/parser/tag_expression_spec.rb +116 -0
  85. data/spec/gherkin/rb_lexer_spec.rb +19 -0
  86. data/spec/gherkin/sexp_recorder.rb +32 -0
  87. data/spec/gherkin/shared/lexer_spec.rb +550 -0
  88. data/spec/gherkin/shared/py_string_spec.rb +150 -0
  89. data/spec/gherkin/shared/row_spec.rb +104 -0
  90. data/spec/gherkin/shared/tags_spec.rb +50 -0
  91. data/spec/spec_helper.rb +87 -0
  92. data/tasks/bench.rake +188 -0
  93. data/tasks/bench/feature_builder.rb +49 -0
  94. data/tasks/bench/generated/.gitignore +1 -0
  95. data/tasks/bench/null_listener.rb +4 -0
  96. data/tasks/compile.rake +89 -0
  97. data/tasks/cucumber.rake +26 -0
  98. data/tasks/gems.rake +45 -0
  99. data/tasks/ikvm.rake +47 -0
  100. data/tasks/ragel_task.rb +70 -0
  101. data/tasks/rdoc.rake +12 -0
  102. data/tasks/release.rake +26 -0
  103. data/tasks/rspec.rake +15 -0
  104. 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,9 @@
1
+ module Gherkin
2
+ module Formatter
3
+ class MonochromeFormat
4
+ def format_argument(arg)
5
+ arg
6
+ end
7
+ end
8
+ end
9
+ 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
@@ -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