gherkin 1.0.2-i386-mingw32 → 1.0.3-i386-mingw32

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 (76) hide show
  1. data/.gitattributes +1 -0
  2. data/History.txt +18 -0
  3. data/README.rdoc +12 -1
  4. data/Rakefile +4 -2
  5. data/VERSION.yml +1 -1
  6. data/bin/gherkin +1 -1
  7. data/dotnet/.gitignore +13 -0
  8. data/features/feature_parser.feature +22 -2
  9. data/features/native_lexer.feature +1 -1
  10. data/features/parser_with_native_lexer.feature +1 -1
  11. data/features/step_definitions/gherkin_steps.rb +2 -6
  12. data/features/step_definitions/pretty_printer_steps.rb +2 -3
  13. data/features/steps_parser.feature +1 -1
  14. data/gherkin.gemspec +46 -18
  15. data/java/Gherkin.iml +2 -4
  16. data/java/build.xml +3 -0
  17. data/java/src/gherkin/FixJava.java +6 -3
  18. data/java/src/gherkin/I18nLexer.java +48 -0
  19. data/java/src/gherkin/Listener.java +3 -1
  20. data/java/src/gherkin/Main.java +17 -0
  21. data/java/src/gherkin/Parser.java +9 -3
  22. data/java/src/gherkin/formatter/Argument.java +39 -0
  23. data/java/src/gherkin/formatter/ArgumentFormat.java +17 -0
  24. data/java/src/gherkin/formatter/Colors.java +7 -0
  25. data/java/src/gherkin/formatter/Formatter.java +15 -0
  26. data/java/src/gherkin/formatter/PrettyFormatter.java +219 -0
  27. data/java/src/gherkin/parser/StateMachineReader.java +8 -3
  28. data/java/test/gherkin/formatter/ArgumentTest.java +17 -0
  29. data/lib/gherkin/csharp_lexer.rb +15 -0
  30. data/lib/gherkin/format/argument.rb +35 -0
  31. data/lib/gherkin/format/monochrome_format.rb +9 -0
  32. data/lib/gherkin/i18n.rb +22 -0
  33. data/lib/gherkin/i18n.yml +34 -20
  34. data/lib/gherkin/i18n_lexer.rb +57 -13
  35. data/lib/gherkin/lexer.rb +9 -18
  36. data/lib/gherkin/parser.rb +3 -3
  37. data/lib/gherkin/parser/meta.txt +5 -4
  38. data/lib/gherkin/parser/root.txt +11 -9
  39. data/lib/gherkin/parser/steps.txt +4 -3
  40. data/lib/gherkin/rb_parser.rb +13 -5
  41. data/lib/gherkin/tools/colors.rb +119 -0
  42. data/lib/gherkin/tools/files.rb +6 -1
  43. data/lib/gherkin/tools/pretty_listener.rb +115 -23
  44. data/ragel/lexer.c.rl.erb +67 -51
  45. data/ragel/lexer.csharp.rl.erb +240 -0
  46. data/ragel/lexer.java.rl.erb +27 -18
  47. data/ragel/lexer.rb.rl.erb +17 -17
  48. data/ragel/lexer_common.rl.erb +8 -8
  49. data/spec/gherkin/c_lexer_spec.rb +4 -4
  50. data/spec/gherkin/csharp_lexer_spec.rb +20 -0
  51. data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
  52. data/spec/gherkin/fixtures/complex.feature +2 -0
  53. data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
  54. data/spec/gherkin/fixtures/i18n_fr.feature +1 -0
  55. data/spec/gherkin/fixtures/i18n_no.feature +1 -0
  56. data/spec/gherkin/fixtures/i18n_zh-CN.feature +1 -0
  57. data/spec/gherkin/format/argument_spec.rb +28 -0
  58. data/spec/gherkin/i18n_lexer_spec.rb +4 -4
  59. data/spec/gherkin/i18n_spec.rb +31 -23
  60. data/spec/gherkin/java_lexer_spec.rb +4 -3
  61. data/spec/gherkin/parser_spec.rb +5 -0
  62. data/spec/gherkin/rb_lexer_spec.rb +4 -2
  63. data/spec/gherkin/sexp_recorder.rb +1 -1
  64. data/spec/gherkin/shared/lexer_spec.rb +169 -60
  65. data/spec/gherkin/shared/py_string_spec.rb +6 -0
  66. data/spec/gherkin/shared/row_spec.rb +107 -0
  67. data/spec/gherkin/shared/tags_spec.rb +1 -1
  68. data/spec/gherkin/tools/colors_spec.rb +19 -0
  69. data/spec/gherkin/tools/pretty_listener_spec.rb +147 -0
  70. data/spec/spec_helper.rb +31 -7
  71. data/tasks/compile.rake +81 -7
  72. data/tasks/ragel_task.rb +6 -4
  73. data/tasks/rspec.rake +2 -2
  74. metadata +92 -31
  75. data/lib/gherkin/java_lexer.rb +0 -10
  76. data/spec/gherkin/shared/table_spec.rb +0 -97
@@ -77,7 +77,7 @@ module Gherkin
77
77
 
78
78
  def expected
79
79
  allowed = @transition_map[@state].find_all { |_, action| action != "E" }
80
- allowed.collect { |state| state[0] }.sort
80
+ allowed.collect { |state| state[0] }.sort - ['eof']
81
81
  end
82
82
 
83
83
  private
@@ -100,18 +100,26 @@ module Gherkin
100
100
 
101
101
  def transition_table(name)
102
102
  state_machine_reader = StateMachineReader.new
103
- lexer = Gherkin::Lexer['en'].new(state_machine_reader)
103
+ lexer = Gherkin::I18nLexer.lexer_class('en', false).new(state_machine_reader)
104
104
  lexer.scan(File.read(File.dirname(__FILE__) + "/parser/#{name}.txt"))
105
105
  state_machine_reader.rows
106
106
  end
107
107
 
108
108
  class StateMachineReader
109
109
  attr_reader :rows
110
- def table(rows, line_number)
111
- @rows = rows
110
+
111
+ def initialize
112
+ @rows = []
113
+ end
114
+
115
+ def row(row, line_number)
116
+ @rows << row
117
+ end
118
+
119
+ def eof
112
120
  end
113
121
  end
114
122
 
115
123
  end
116
124
  end
117
- end
125
+ end
@@ -0,0 +1,119 @@
1
+ require 'term/ansicolor'
2
+
3
+ module Gherkin
4
+ module Tools
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
@@ -23,7 +23,12 @@ module Gherkin
23
23
  def scan(file, listener)
24
24
  parser = Parser.new(listener, true)
25
25
  lexer = I18nLexer.new(parser)
26
- lexer.scan(IO.read(file))
26
+ begin
27
+ lexer.scan(IO.read(file))
28
+ rescue => e
29
+ e.message << " (#{file})"
30
+ raise e
31
+ end
27
32
  end
28
33
  end
29
34
  end
@@ -1,10 +1,32 @@
1
1
  # encoding: utf-8
2
+
3
+ require 'gherkin/tools/colors'
4
+ require 'gherkin/format/monochrome_format'
5
+
2
6
  module Gherkin
3
7
  module Tools
8
+ # TODO: Rename to Gherkin::Pretty::PrettyReporter - that's what this class *does*
9
+ # (The fact that it conforms to the Gherkin Listener interface is secondary)
4
10
  class PrettyListener
5
- def initialize(io)
11
+ include Gherkin::Tools::Colors
12
+
13
+ class << self
14
+ def new(io, monochrome=false)
15
+ if defined?(JRUBY_VERSION)
16
+ require 'gherkin.jar'
17
+ Java::GherkinFormatter::PrettyFormatter.new(io, monochrome)
18
+ else
19
+ super
20
+ end
21
+ end
22
+ end
23
+
24
+ def initialize(io, monochrome=false)
6
25
  @io = io
26
+ @monochrome = monochrome
27
+ @format = @monochrome ? Format::MonochromeFormat.new : Format::AnsiColorFormat.new
7
28
  @tags = nil
29
+ @comments = nil
8
30
  end
9
31
 
10
32
  def tag(name, line)
@@ -13,45 +35,48 @@ module Gherkin
13
35
  end
14
36
 
15
37
  def comment(content, line)
16
- @io.puts content
38
+ @comments ||= []
39
+ @comments << content
17
40
  end
18
41
 
19
42
  def feature(keyword, name, line)
20
- tags = @tags ? @tags.join(' ') + "\n" : ''
21
- @tags = nil
22
- @io.puts "#{tags}#{keyword}: #{indent(name, ' ')}"
43
+ @io.puts "#{grab_comments!('')}#{grab_tags!('')}#{keyword}: #{indent(name, ' ')}"
23
44
  end
24
45
 
25
46
  def background(keyword, name, line)
26
- @io.puts "\n #{keyword}: #{indent(name, ' ')}"
47
+ @io.puts "\n#{grab_comments!(' ')} #{keyword}: #{indent(name, ' ')}"
27
48
  end
28
49
 
29
- def scenario(keyword, name, line)
30
- tags = @tags ? ' ' + @tags.join(' ') + "\n" : ''
31
- @tags = nil
32
- @io.puts "\n#{tags} #{keyword}: #{indent(name, ' ')}"
50
+ def scenario(keyword, name, line, location=nil)
51
+ flush_table
52
+ @io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}#{indented_scenario_location!(keyword, name, location)}"
33
53
  end
34
54
 
35
55
  def scenario_outline(keyword, name, line)
36
- tags = @tags ? ' ' + @tags.join(' ') + "\n" : ''
37
- @tags = nil
38
- @io.puts "\n#{tags} #{keyword}: #{indent(name, ' ')}"
56
+ flush_table
57
+ @io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
39
58
  end
40
59
 
41
60
  def examples(keyword, name, line)
42
- @io.puts "\n #{keyword}: #{indent(name, ' ')}"
61
+ flush_table
62
+ @io.puts "\n#{grab_comments!(' ')}#{grab_tags!(' ')} #{keyword}: #{indent(name, ' ')}"
43
63
  end
44
64
 
45
- def step(keyword, name, line)
46
- @io.puts " #{keyword} #{indent(name, ' ')}"
65
+ def step(keyword, name, line, status=nil, arguments=nil, location=nil)
66
+ flush_table
67
+ status_param = "#{status}_param" if status
68
+ name = Gherkin::Format::Argument.format(name, @format, (arguments || []))
69
+ #{|arg| status_param ? self.__send__(status_param, arg, @monochrome) : arg} if arguments
70
+
71
+ step = "#{keyword}#{indent(name, ' ')}"
72
+ step = self.__send__(status, step, @monochrome) if status
73
+
74
+ @io.puts("#{grab_comments!(' ')} #{step}#{indented_step_location!(location)}")
47
75
  end
48
76
 
49
- def table(rows, line)
50
- rows = rows.to_a.map {|row| row.to_a} if defined?(JRUBY_VERSION) # Convert ArrayList
51
- max_lengths = rows.transpose.map { |col| col.map { |cell| cell.unpack("U*").length }.max }.flatten
52
- rows.each do |table_line|
53
- @io.puts ' | ' + table_line.zip(max_lengths).map { |cell, max_length| cell + ' ' * (max_length-cell.unpack("U*").length) }.join(' | ') + ' |'
54
- end
77
+ def row(row, line)
78
+ @rows ||= []
79
+ @rows << row
55
80
  end
56
81
 
57
82
  def py_string(string, line)
@@ -62,7 +87,48 @@ module Gherkin
62
87
  raise "SYNTAX ERROR"
63
88
  end
64
89
 
65
- private
90
+ def eof
91
+ flush_table
92
+ end
93
+
94
+ # This method can be invoked before a #scenario, to ensure location arguments are aligned
95
+ def steps(steps)
96
+ @step_lengths = steps.map {|keyword, name| (keyword+name).unpack("U*").length}
97
+ @max_step_length = @step_lengths.max
98
+ @step_index = -1
99
+ end
100
+
101
+ def exception(exception)
102
+ exception_text = "#{exception.message} (#{exception.class})\n#{(exception.backtrace || []).join("\n")}".gsub(/^/, ' ')
103
+ @io.puts(failed(exception_text, @monochrome))
104
+ end
105
+
106
+ def flush_table(exception=nil, statuses=nil)
107
+ return if @rows.nil?
108
+ cell_lengths = @rows.map { |col| col.map { |cell| cell.unpack("U*").length }}
109
+ max_lengths = cell_lengths.transpose.map { |col_lengths| col_lengths.max }.flatten
110
+
111
+ @rows.each_with_index do |row, i|
112
+ j = -1
113
+ @io.puts ' | ' + row.zip(max_lengths).map { |cell, max_length|
114
+ j += 1
115
+ color(cell, statuses, j) + ' ' * (max_length - cell_lengths[i][j])
116
+ }.join(' | ') + ' |'
117
+ exception(exception) if exception
118
+ end
119
+ @rows = nil
120
+ end
121
+
122
+ private
123
+
124
+ def color(cell, statuses, col)
125
+ if statuses
126
+ self.__send__(statuses[col], cell, @monochrome) + (@monochrome ? '' : reset)
127
+ else
128
+ cell
129
+ end
130
+ end
131
+
66
132
  if(RUBY_VERSION =~ /^1\.9/)
67
133
  START = /#{"^".encode('UTF-8')}/
68
134
  NL = Regexp.new("\n".encode('UTF-8'))
@@ -79,6 +145,32 @@ module Gherkin
79
145
  s
80
146
  end.join("\n")
81
147
  end
148
+
149
+ def grab_tags!(indent)
150
+ tags = @tags ? indent + @tags.join(' ') + "\n" : ''
151
+ @tags = nil
152
+ tags
153
+ end
154
+
155
+ def grab_comments!(indent)
156
+ comments = @comments ? indent + @comments.join("\n#{indent}") + "\n" : ''
157
+ @comments = nil
158
+ comments
159
+ end
160
+
161
+ def indented_scenario_location!(keyword, name, location)
162
+ return "" if location.nil?
163
+ l = (keyword+name).unpack("U*").length
164
+ @max_step_length = [@max_step_length, l].max
165
+ indent = @max_step_length - l
166
+ ' ' * indent + ' ' + comments("# #{location}", @monochrome)
167
+ end
168
+
169
+ def indented_step_location!(location)
170
+ return "" if location.nil?
171
+ indent = @max_step_length - @step_lengths[@step_index+=1]
172
+ ' ' * indent + ' ' + comments("# #{location}", @monochrome)
173
+ end
82
174
  end
83
175
  end
84
176
  end
@@ -14,12 +14,17 @@
14
14
  #ifdef HAVE_RUBY_ENCODING_H
15
15
  #include <ruby/encoding.h>
16
16
  #define ENCODED_STR_NEW(ptr, len) \
17
- rb_enc_str_new(ptr, len, rb_utf8_encoding());
17
+ rb_enc_str_new(ptr, len, rb_utf8_encoding())
18
18
  #else
19
19
  #define ENCODED_STR_NEW(ptr, len) \
20
- rb_str_new(ptr, len);
20
+ rb_str_new(ptr, len)
21
21
  #endif
22
22
 
23
+ #define LF_FLAG 0
24
+ #define CRLF_FLAG 1
25
+ #define LF "\n"
26
+ #define CRLF "\r\n"
27
+
23
28
  #ifndef RSTRING_PTR
24
29
  #define RSTRING_PTR(s) (RSTRING(s)->ptr)
25
30
  #endif
@@ -39,23 +44,22 @@ typedef struct lexer_state {
39
44
  int line_number;
40
45
  int current_line;
41
46
  int start_col;
47
+ int eol;
42
48
  size_t mark;
43
49
  size_t keyword_start;
44
50
  size_t keyword_end;
45
51
  size_t next_keyword_start;
46
52
  size_t content_start;
47
53
  size_t content_end;
48
- size_t field_len;
49
54
  size_t query_start;
50
55
  size_t last_newline;
51
56
  size_t final_newline;
52
57
  } lexer_state;
53
58
 
54
59
  static VALUE mGherkin;
55
- static VALUE mLexer;
56
60
  static VALUE mCLexer;
57
61
  static VALUE cI18nLexer;
58
- static VALUE rb_eGherkinLexerError;
62
+ static VALUE rb_eGherkinLexingError;
59
63
 
60
64
  #define LEN(AT, P) (P - data - lexer->AT)
61
65
  #define MARK(M, P) (lexer->M = (P) - data)
@@ -65,16 +69,16 @@ static VALUE rb_eGherkinLexerError;
65
69
  store_kw_con(listener, # EVENT, \
66
70
  PTR_TO(keyword_start), LEN(keyword_start, PTR_TO(keyword_end - 1)), \
67
71
  PTR_TO(content_start), LEN(content_start, PTR_TO(content_end)), \
68
- lexer->current_line); \
72
+ lexer->current_line, lexer->eol); \
69
73
  if (lexer->content_end != 0) { \
70
74
  p = PTR_TO(content_end - 1); \
71
75
  } \
72
- lexer->content_end = 0;
76
+ lexer->content_end = 0
73
77
 
74
78
  #define STORE_ATTR(ATTR) \
75
79
  store_attr(listener, # ATTR, \
76
80
  PTR_TO(content_start), LEN(content_start, p), \
77
- lexer->line_number);
81
+ lexer->line_number)
78
82
 
79
83
  %%{
80
84
  machine lexer;
@@ -102,39 +106,39 @@ static VALUE rb_eGherkinLexerError;
102
106
  }
103
107
 
104
108
  action store_feature_content {
105
- STORE_KW_END_CON(feature)
109
+ STORE_KW_END_CON(feature);
106
110
  }
107
111
 
108
112
  action store_background_content {
109
- STORE_KW_END_CON(background)
113
+ STORE_KW_END_CON(background);
110
114
  }
111
115
 
112
116
  action store_scenario_content {
113
- STORE_KW_END_CON(scenario)
117
+ STORE_KW_END_CON(scenario);
114
118
  }
115
119
 
116
120
  action store_scenario_outline_content {
117
- STORE_KW_END_CON(scenario_outline)
121
+ STORE_KW_END_CON(scenario_outline);
118
122
  }
119
123
 
120
124
  action store_examples_content {
121
- STORE_KW_END_CON(examples)
125
+ STORE_KW_END_CON(examples);
122
126
  }
123
127
 
124
128
  action store_step_content {
125
129
  store_kw_con(listener, "step",
126
130
  PTR_TO(keyword_start), LEN(keyword_start, PTR_TO(keyword_end)),
127
131
  PTR_TO(content_start), LEN(content_start, p),
128
- lexer->current_line);
132
+ lexer->current_line, lexer->eol);
129
133
  }
130
134
 
131
135
  action store_comment_content {
132
- STORE_ATTR(comment)
136
+ STORE_ATTR(comment);
133
137
  lexer->mark = 0;
134
138
  }
135
139
 
136
140
  action store_tag_content {
137
- STORE_ATTR(tag)
141
+ STORE_ATTR(tag);
138
142
  lexer->mark = 0;
139
143
  }
140
144
 
@@ -164,11 +168,10 @@ static VALUE rb_eGherkinLexerError;
164
168
  MARK(content_end, p);
165
169
  }
166
170
 
167
- action start_table {
171
+ action start_row {
168
172
  p = p - 1;
169
173
  lexer->current_line = lexer->line_number;
170
- rb_ary_clear(rows);
171
- rb_ary_clear(current_row);
174
+ current_row = rb_ary_new();
172
175
  }
173
176
 
174
177
  action begin_cell_content {
@@ -176,29 +179,20 @@ static VALUE rb_eGherkinLexerError;
176
179
  }
177
180
 
178
181
  action store_cell_content {
179
- VALUE con = Qnil;
180
- con = ENCODED_STR_NEW(PTR_TO(content_start), LEN(content_start, p));
182
+ VALUE con = ENCODED_STR_NEW(PTR_TO(content_start), LEN(content_start, p));
181
183
  rb_funcall(con, rb_intern("strip!"), 0);
182
184
 
183
185
  rb_ary_push(current_row, con);
184
186
  }
185
187
 
186
- action start_row {
187
- current_row = rb_ary_new();
188
- }
189
-
190
188
  action store_row {
191
- rb_ary_push(rows, current_row);
192
- }
193
-
194
- action store_table {
195
- rb_funcall(listener, rb_intern("table"), 2, rows, INT2FIX(lexer->current_line));
189
+ rb_funcall(listener, rb_intern("row"), 2, current_row, INT2FIX(lexer->current_line));
196
190
  }
197
191
 
198
192
  action end_feature {
199
193
  if (cs < lexer_first_final) {
200
194
  if (raise_lexer_error != NULL) {
201
- int count = 0;
195
+ size_t count = 0;
202
196
  int newstr_count = 0;
203
197
  size_t len;
204
198
  const char *buff;
@@ -229,8 +223,10 @@ static VALUE rb_eGherkinLexerError;
229
223
 
230
224
  int line = lexer->line_number;
231
225
  lexer_init(lexer); // Re-initialize so we can scan again with the same lexer
232
- raise_lexer_error(listener, newstr, line);
226
+ raise_lexer_error(newstr, line);
233
227
  }
228
+ } else {
229
+ rb_funcall(listener, rb_intern("eof"), 0);
234
230
  }
235
231
  }
236
232
 
@@ -251,28 +247,28 @@ strip_i(VALUE str, VALUE ary)
251
247
  }
252
248
 
253
249
  static VALUE
254
- multiline_strip(VALUE text)
250
+ multiline_strip(VALUE text, int eol)
255
251
  {
256
252
  VALUE map = rb_ary_new();
257
253
  VALUE split = rb_str_split(text, "\n");
258
254
 
259
255
  rb_iterate(rb_each, split, strip_i, map);
260
256
 
261
- return rb_ary_join(split, rb_str_new2("\n"));
257
+ return rb_ary_join(split, rb_str_new2( \
258
+ eol == CRLF_FLAG ? CRLF : LF ));
262
259
  }
263
260
 
264
261
  static void
265
262
  store_kw_con(VALUE listener, const char * event_name,
266
263
  const char * keyword_at, size_t keyword_length,
267
264
  const char * at, size_t length,
268
- int current_line)
265
+ int current_line, int eol)
269
266
  {
270
267
  VALUE con = Qnil, kw = Qnil;
271
268
  kw = ENCODED_STR_NEW(keyword_at, keyword_length);
272
269
  con = ENCODED_STR_NEW(at, length);
273
- con = multiline_strip(con);
270
+ con = multiline_strip(con, eol);
274
271
  rb_funcall(con, rb_intern("strip!"), 0);
275
- rb_funcall(kw, rb_intern("strip!"), 0);
276
272
  rb_funcall(listener, rb_intern(event_name), 3, kw, con, INT2FIX(current_line));
277
273
  }
278
274
 
@@ -296,14 +292,30 @@ store_pystring_content(VALUE listener,
296
292
  char pat[32];
297
293
  snprintf(pat, 32, "^ {0,%d}", start_col);
298
294
  VALUE re = rb_reg_regcomp(rb_str_new2(pat));
295
+ VALUE re2 = rb_reg_regcomp(rb_str_new2("\r\\Z"));
299
296
  rb_funcall(con, rb_intern("gsub!"), 2, re, rb_str_new2(""));
297
+ rb_funcall(con, rb_intern("sub!"), 2, re2, rb_str_new2(""));
300
298
  rb_funcall(listener, rb_intern("py_string"), 2, con, INT2FIX(current_line));
301
299
  }
302
300
 
303
301
  static void
304
- raise_lexer_error(VALUE listener, const char * at, int line)
302
+ raise_lexer_error(const char * at, int line)
305
303
  {
306
- rb_raise(rb_eGherkinLexerError, "Lexing error on line %d: '%s'.", line, at);
304
+ rb_raise(rb_eGherkinLexingError, "Lexing error on line %d: '%s'.", line, at);
305
+ }
306
+
307
+ static int
308
+ count_char(char char_to_count, char *str) {
309
+
310
+ int count = 0;
311
+ int i = 0;
312
+ while(str[i] != '\0') {
313
+ if(str[i] == char_to_count) {
314
+ count++;
315
+ }
316
+ i++;
317
+ }
318
+ return count;
307
319
  }
308
320
 
309
321
  static void lexer_init(lexer_state *lexer) {
@@ -311,7 +323,6 @@ static void lexer_init(lexer_state *lexer) {
311
323
  lexer->content_end = 0;
312
324
  lexer->content_len = 0;
313
325
  lexer->mark = 0;
314
- lexer->field_len = 0;
315
326
  lexer->keyword_start = 0;
316
327
  lexer->keyword_end = 0;
317
328
  lexer->next_keyword_start = 0;
@@ -319,6 +330,7 @@ static void lexer_init(lexer_state *lexer) {
319
330
  lexer->last_newline = 0;
320
331
  lexer->final_newline = 0;
321
332
  lexer->start_col = 0;
333
+ lexer->eol = LF_FLAG;
322
334
  }
323
335
 
324
336
  static VALUE CLexer_alloc(VALUE klass)
@@ -348,41 +360,45 @@ static VALUE CLexer_scan(VALUE self, VALUE input)
348
360
  lexer_state *lexer = NULL;
349
361
  DATA_GET(self, lexer_state, lexer);
350
362
 
363
+
351
364
  VALUE input_copy = rb_str_dup(input);
365
+
352
366
  rb_str_append(input_copy, rb_str_new2("\n%_FEATURE_END_%"));
353
367
  char *data = RSTRING_PTR(input_copy);
354
- long len = RSTRING_LEN(input_copy);
368
+ size_t len = RSTRING_LEN(input_copy);
369
+
370
+ if (count_char('\r', data) > (count_char('\n', data) / 2)) {
371
+ lexer->eol = CRLF_FLAG;
372
+ }
355
373
 
356
374
  if (len == 0) {
357
- rb_raise(rb_eGherkinLexerError, "No content to lex.");
375
+ rb_raise(rb_eGherkinLexingError, "No content to lex.");
358
376
  } else {
377
+
359
378
  const char *p, *pe, *eof;
360
379
  int cs = 0;
361
380
 
362
381
  VALUE listener = rb_iv_get(self, "@listener");
363
- VALUE rows = rb_ary_new();
364
- VALUE current_row = rb_ary_new();
365
-
382
+ VALUE current_row = Qnil;
383
+
366
384
  p = data;
367
385
  pe = data + len;
368
386
  eof = pe;
369
387
 
370
388
  assert(*pe == '\0' && "pointer does not end on NULL");
371
- assert(pe - p == len && "pointers aren't same distance");
372
389
 
373
390
  %% write init;
374
391
  %% write exec;
375
-
392
+
376
393
  assert(p <= pe && "data overflow after parsing execute");
377
394
  assert(lexer->content_start <= len && "content starts after data end");
378
395
  assert(lexer->mark < len && "mark is after data end");
379
- assert(lexer->field_len <= len && "field has length longer than the whole data");
380
396
 
381
397
  // Reset lexer by re-initializing the whole thing
382
398
  lexer_init(lexer);
383
399
 
384
400
  if (cs == lexer_error) {
385
- rb_raise(rb_eGherkinLexerError, "Invalid format, lexing fails.");
401
+ rb_raise(rb_eGherkinLexingError, "Invalid format, lexing fails.");
386
402
  } else {
387
403
  return Qtrue;
388
404
  }
@@ -392,8 +408,7 @@ static VALUE CLexer_scan(VALUE self, VALUE input)
392
408
  void Init_gherkin_lexer_<%= @i18n.sanitized_key %>()
393
409
  {
394
410
  mGherkin = rb_define_module("Gherkin");
395
- mLexer = rb_const_get(mGherkin, rb_intern("Lexer"));
396
- rb_eGherkinLexerError = rb_const_get(mLexer, rb_intern("LexingError"));
411
+ rb_eGherkinLexingError = rb_const_get(mGherkin, rb_intern("LexingError"));
397
412
 
398
413
  mCLexer = rb_define_module_under(mGherkin, "CLexer");
399
414
  cI18nLexer = rb_define_class_under(mCLexer, "<%= @i18n.sanitized_key.capitalize %>", rb_cObject);
@@ -401,3 +416,4 @@ void Init_gherkin_lexer_<%= @i18n.sanitized_key %>()
401
416
  rb_define_method(cI18nLexer, "initialize", CLexer_init, 1);
402
417
  rb_define_method(cI18nLexer, "scan", CLexer_scan, 1);
403
418
  }
419
+