gherkin 1.0.2-java → 1.0.3-java
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitattributes +1 -0
- data/History.txt +18 -0
- data/README.rdoc +4 -1
- data/Rakefile +4 -2
- data/VERSION.yml +1 -1
- data/bin/gherkin +1 -1
- data/dotnet/.gitignore +13 -0
- data/features/feature_parser.feature +22 -2
- data/features/native_lexer.feature +1 -1
- data/features/parser_with_native_lexer.feature +1 -1
- data/features/step_definitions/gherkin_steps.rb +2 -6
- data/features/step_definitions/pretty_printer_steps.rb +2 -3
- data/features/steps_parser.feature +1 -1
- data/gherkin.gemspec +40 -13
- data/java/Gherkin.iml +2 -4
- data/java/build.xml +3 -0
- data/java/src/gherkin/FixJava.java +6 -3
- data/java/src/gherkin/I18nLexer.java +48 -0
- data/java/src/gherkin/Listener.java +3 -1
- data/java/src/gherkin/Main.java +17 -0
- data/java/src/gherkin/Parser.java +9 -3
- data/java/src/gherkin/formatter/Argument.java +39 -0
- data/java/src/gherkin/formatter/ArgumentFormat.java +17 -0
- data/java/src/gherkin/formatter/Colors.java +7 -0
- data/java/src/gherkin/formatter/Formatter.java +15 -0
- data/java/src/gherkin/formatter/PrettyFormatter.java +219 -0
- data/java/src/gherkin/parser/StateMachineReader.java +8 -3
- data/java/test/gherkin/formatter/ArgumentTest.java +17 -0
- data/lib/gherkin/csharp_lexer.rb +15 -0
- data/lib/gherkin/format/argument.rb +35 -0
- data/lib/gherkin/format/monochrome_format.rb +9 -0
- data/lib/gherkin/i18n.rb +22 -0
- data/lib/gherkin/i18n.yml +34 -20
- data/lib/gherkin/i18n_lexer.rb +57 -13
- data/lib/gherkin/lexer.rb +9 -18
- data/lib/gherkin/parser.rb +3 -3
- data/lib/gherkin/parser/meta.txt +5 -4
- data/lib/gherkin/parser/root.txt +11 -9
- data/lib/gherkin/parser/steps.txt +4 -3
- data/lib/gherkin/rb_parser.rb +13 -5
- data/lib/gherkin/tools/colors.rb +119 -0
- data/lib/gherkin/tools/files.rb +6 -1
- data/lib/gherkin/tools/pretty_listener.rb +115 -23
- data/ragel/lexer.c.rl.erb +67 -51
- data/ragel/lexer.csharp.rl.erb +240 -0
- data/ragel/lexer.java.rl.erb +27 -18
- data/ragel/lexer.rb.rl.erb +17 -17
- data/ragel/lexer_common.rl.erb +8 -8
- data/spec/gherkin/c_lexer_spec.rb +4 -4
- data/spec/gherkin/csharp_lexer_spec.rb +20 -0
- data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
- data/spec/gherkin/fixtures/complex.feature +2 -0
- data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
- data/spec/gherkin/fixtures/i18n_fr.feature +1 -0
- data/spec/gherkin/fixtures/i18n_no.feature +1 -0
- data/spec/gherkin/fixtures/i18n_zh-CN.feature +1 -0
- data/spec/gherkin/format/argument_spec.rb +28 -0
- data/spec/gherkin/i18n_lexer_spec.rb +4 -4
- data/spec/gherkin/i18n_spec.rb +31 -23
- data/spec/gherkin/java_lexer_spec.rb +4 -3
- data/spec/gherkin/parser_spec.rb +5 -0
- data/spec/gherkin/rb_lexer_spec.rb +4 -2
- data/spec/gherkin/sexp_recorder.rb +1 -1
- data/spec/gherkin/shared/lexer_spec.rb +169 -60
- data/spec/gherkin/shared/py_string_spec.rb +6 -0
- data/spec/gherkin/shared/row_spec.rb +107 -0
- data/spec/gherkin/shared/tags_spec.rb +1 -1
- data/spec/gherkin/tools/colors_spec.rb +19 -0
- data/spec/gherkin/tools/pretty_listener_spec.rb +147 -0
- data/spec/spec_helper.rb +31 -7
- data/tasks/compile.rake +81 -7
- data/tasks/ragel_task.rb +6 -4
- data/tasks/rspec.rake +2 -2
- metadata +86 -26
- data/lib/gherkin/java_lexer.rb +0 -10
- data/spec/gherkin/shared/table_spec.rb +0 -97
data/lib/gherkin/tools/files.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
-
@
|
38
|
+
@comments ||= []
|
39
|
+
@comments << content
|
17
40
|
end
|
18
41
|
|
19
42
|
def feature(keyword, name, line)
|
20
|
-
|
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
|
-
|
31
|
-
@
|
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
|
-
|
37
|
-
@
|
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
|
-
|
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
|
-
|
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
|
50
|
-
rows
|
51
|
-
|
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
|
-
|
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
|
data/ragel/lexer.c.rl.erb
CHANGED
@@ -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
|
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
|
171
|
+
action start_row {
|
168
172
|
p = p - 1;
|
169
173
|
lexer->current_line = lexer->line_number;
|
170
|
-
|
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 =
|
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
|
-
|
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
|
-
|
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(
|
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(
|
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(
|
302
|
+
raise_lexer_error(const char * at, int line)
|
305
303
|
{
|
306
|
-
rb_raise(
|
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
|
-
|
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(
|
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
|
364
|
-
|
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(
|
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
|
-
|
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
|
+
|