gherkin 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/.gitattributes +1 -0
  2. data/History.txt +18 -0
  3. data/README.rdoc +4 -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 +53 -24
  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 +104 -41
  75. data/lib/gherkin/java_lexer.rb +0 -10
  76. data/spec/gherkin/shared/table_spec.rb +0 -97
@@ -17,14 +17,17 @@ public class FixJava {
17
17
  return sb.toString();
18
18
  }
19
19
 
20
- public static String readResourceAsString(String filePath) throws IOException {
21
- Reader machine = new InputStreamReader(FixJava.class.getResourceAsStream(filePath));
20
+ public static String readResource(String filePath) throws IOException {
21
+ Reader reader = new InputStreamReader(FixJava.class.getResourceAsStream(filePath));
22
+ return readReader(reader);
23
+ }
22
24
 
25
+ public static String readReader(Reader reader) throws IOException {
23
26
  final char[] buffer = new char[0x10000];
24
27
  StringBuilder sb = new StringBuilder();
25
28
  int read;
26
29
  do {
27
- read = machine.read(buffer, 0, buffer.length);
30
+ read = reader.read(buffer, 0, buffer.length);
28
31
  if (read > 0) {
29
32
  sb.append(buffer, 0, read);
30
33
  }
@@ -0,0 +1,48 @@
1
+ package gherkin;
2
+
3
+ import java.util.regex.Matcher;
4
+ import java.util.regex.Pattern;
5
+
6
+ public class I18nLexer implements Lexer {
7
+ private static final Pattern LANGUAGE_PATTERN = Pattern.compile("language\\s*:\\s*(.*)");
8
+ private final Listener listener;
9
+ private String i18nLanguage;
10
+
11
+ public I18nLexer(Listener listener) {
12
+ this.listener = listener;
13
+ }
14
+
15
+ /**
16
+ * @return the i18n language code from the previous scanned source.
17
+ */
18
+ public String getI18nLanguage() {
19
+ return i18nLanguage;
20
+ }
21
+
22
+ public void scan(CharSequence source) {
23
+ createDelegate(source).scan(source);
24
+ }
25
+
26
+ private Lexer createDelegate(CharSequence source) {
27
+ i18nLanguage = lang(source);
28
+ String i18nLanguage = this.i18nLanguage.replaceAll("[\\s-]", "").toLowerCase();
29
+ String i18nLexerClassName = i18nLanguage.substring(0,1).toUpperCase() + i18nLanguage.substring(1);
30
+ String qualifiedI18nLexerClassName = "gherkin.lexer." + i18nLexerClassName;
31
+ try {
32
+ Class<?> delegateClass = Class.forName(qualifiedI18nLexerClassName);
33
+ return (Lexer) delegateClass.getConstructor(Listener.class).newInstance(listener);
34
+ } catch (Exception e) {
35
+ throw new RuntimeException("Couldn't load lexer class: " + qualifiedI18nLexerClassName, e);
36
+ }
37
+ }
38
+
39
+ private String lang(CharSequence source) {
40
+ String lineOne = source.toString().split("\\n")[0];
41
+ Matcher matcher = LANGUAGE_PATTERN.matcher(lineOne);
42
+ if(matcher.find()) {
43
+ return matcher.group(1);
44
+ } else {
45
+ return "en";
46
+ }
47
+ }
48
+ }
@@ -19,9 +19,11 @@ public interface Listener {
19
19
 
20
20
  void step(String keyword, String name, int line);
21
21
 
22
- void table(List<List<String>> rows, int line);
22
+ void row(List<String> row, int line);
23
23
 
24
24
  void py_string(String string, int line);
25
25
 
26
+ void eof();
27
+
26
28
  void syntax_error(String state, String event, List<String> legalEvents, int line);
27
29
  }
@@ -0,0 +1,17 @@
1
+ package gherkin;
2
+
3
+ import gherkin.formatter.PrettyFormatter;
4
+
5
+ import java.io.FileReader;
6
+ import java.io.IOException;
7
+
8
+ public class Main {
9
+ public static void main(String[] args) throws IOException {
10
+ Parser p = new Parser(new PrettyFormatter(System.out, true));
11
+ Lexer l = new I18nLexer(p);
12
+
13
+ CharSequence input = FixJava.readReader(new FileReader(args[0]));
14
+ l.scan(input);
15
+ }
16
+
17
+ }
@@ -79,11 +79,16 @@ public class Parser implements Listener {
79
79
  listener.comment(content, line);
80
80
  }
81
81
 
82
- public void table(List<List<String>> rows, int line) {
83
- if (event("table", line))
84
- listener.table(rows, line);
82
+ public void row(List<String> row, int line) {
83
+ if (event("row", line))
84
+ listener.row(row, line);
85
85
  }
86
86
 
87
+ public void eof() {
88
+ if (event("eof", 1))
89
+ listener.eof();
90
+ }
91
+
87
92
  public void syntax_error(String name, String event, List<String> strings, int line) {
88
93
  }
89
94
 
@@ -154,6 +159,7 @@ public class Parser implements Listener {
154
159
  }
155
160
  }
156
161
  Collections.sort(result);
162
+ result.remove("eof");
157
163
  return result;
158
164
  }
159
165
 
@@ -0,0 +1,39 @@
1
+ package gherkin.formatter;
2
+
3
+ import java.io.UnsupportedEncodingException;
4
+ import java.util.List;
5
+
6
+ public class Argument {
7
+ private final int byteOffset;
8
+ private final byte[] val;
9
+
10
+ public Argument(int byteOffset, String val) throws UnsupportedEncodingException {
11
+ this.byteOffset = byteOffset;
12
+ this.val = val.getBytes("UTF-8");
13
+ }
14
+
15
+ public static String format(String string, ArgumentFormat format, List<Argument> arguments) throws UnsupportedEncodingException {
16
+ return format(string, format, (Argument[]) arguments.toArray(new Argument[arguments.size()]));
17
+ }
18
+
19
+ public static String format(String string, ArgumentFormat format, Argument... arguments) throws UnsupportedEncodingException {
20
+ byte[] result = string.getBytes("UTF-8");
21
+ int offset = 0, pastOffset = 0;
22
+ for (Argument argument : arguments) {
23
+ if (argument.byteOffset != -1 && argument.byteOffset >= pastOffset) {
24
+ byte[] replacement = format.formatArgument(argument.val);
25
+ int delta = replacement.length - argument.val.length;
26
+ byte[] newResult = new byte[result.length + delta];
27
+
28
+ System.arraycopy(result, 0, newResult, 0, argument.byteOffset+offset);
29
+ System.arraycopy(replacement, 0, newResult, argument.byteOffset+offset, replacement.length);
30
+ int n = argument.byteOffset + argument.val.length + offset;
31
+ System.arraycopy(result, n, newResult, argument.byteOffset + replacement.length, result.length - n);
32
+
33
+ offset += delta;
34
+ result = newResult;
35
+ }
36
+ }
37
+ return new String(result, "UTF-8");
38
+ }
39
+ }
@@ -0,0 +1,17 @@
1
+ package gherkin.formatter;
2
+
3
+ import java.io.UnsupportedEncodingException;
4
+
5
+ public class ArgumentFormat {
6
+ private final String prefix;
7
+ private final String suffix;
8
+
9
+ public ArgumentFormat(String prefix, String suffix) {
10
+ this.prefix = prefix;
11
+ this.suffix = suffix;
12
+ }
13
+
14
+ public byte[] formatArgument(byte[] argument) throws UnsupportedEncodingException {
15
+ return (prefix + new String(argument, "UTF-8") + suffix).getBytes("UTF-8");
16
+ }
17
+ }
@@ -0,0 +1,7 @@
1
+ package gherkin.formatter;
2
+
3
+ public class Colors {
4
+ public static String comments(String s, boolean monochrome) {
5
+ return s;
6
+ }
7
+ }
@@ -0,0 +1,15 @@
1
+ package gherkin.formatter;
2
+
3
+ import gherkin.Listener;
4
+
5
+ import java.util.List;
6
+
7
+ /**
8
+ * This interface extends the Listener interface with extra methods for formatting
9
+ * Gherkin features during execution.
10
+ */
11
+ public interface Formatter extends Listener {
12
+ void scenario(String keyword, String name, int line, String location);
13
+ void step(String keyword, String name, int line, String status, List<Argument> arguments, String location);
14
+ void flushTable();
15
+ }
@@ -0,0 +1,219 @@
1
+ package gherkin.formatter;
2
+
3
+ import java.io.OutputStream;
4
+ import java.io.PrintWriter;
5
+ import java.util.ArrayList;
6
+ import java.util.Collections;
7
+ import java.util.List;
8
+ import java.util.regex.Pattern;
9
+
10
+ import static gherkin.FixJava.join;
11
+ import static gherkin.formatter.Colors.*;
12
+
13
+ public class PrettyFormatter implements Formatter {
14
+ private final PrintWriter out;
15
+ private final boolean monochrome;
16
+ private int maxStepLength = 0;
17
+ private int[] stepLengths;
18
+ private int stepIndex;
19
+ private List<List<String>> rows;
20
+ private List<String> comments;
21
+ private List<String> tags;
22
+
23
+ public PrettyFormatter(OutputStream out, boolean monochrome) {
24
+ this.out = new PrintWriter(out);
25
+ this.monochrome = monochrome;
26
+ }
27
+
28
+ public void tag(String name, int line) {
29
+ if (tags == null) tags = new ArrayList<String>();
30
+ tags.add('@' + name);
31
+ }
32
+
33
+ public void comment(String content, int line) {
34
+ if (comments == null) comments = new ArrayList<String>();
35
+ comments.add(content);
36
+ }
37
+
38
+ public void feature(String keyword, String name, int line) {
39
+ printCommentsAndTags("");
40
+ out.println(keyword + ": " + indent(name, " "));
41
+ out.flush();
42
+ }
43
+
44
+ public void background(String keyword, String name, int line) {
45
+ out.println();
46
+ printCommentsAndTags(" ");
47
+ out.println(" " + keyword + ": " + name);
48
+ }
49
+
50
+ public void scenario(String keyword, String name, int line) {
51
+ scenario(keyword, name, line, null);
52
+ }
53
+
54
+ public void scenario(String keyword, String name, int line, String location) {
55
+ flushTable();
56
+ out.println();
57
+ printCommentsAndTags(" ");
58
+ out.println(" " + keyword + ": " + indent(name, " ") + indentedScenarioLocation(keyword, name, location));
59
+ out.flush();
60
+ }
61
+
62
+ public void scenario_outline(String keyword, String name, int line) {
63
+ flushTable();
64
+ out.println();
65
+ printCommentsAndTags(" ");
66
+ out.println(" " + keyword + ": " + name);
67
+ }
68
+
69
+ public void examples(String keyword, String name, int line) {
70
+ flushTable();
71
+ out.println();
72
+ printCommentsAndTags(" ");
73
+ out.println(" " + keyword + ": " + name);
74
+ }
75
+
76
+ public void step(String keyword, String name, int line, String status, List<Argument> arguments, String location) {
77
+ flushTable();
78
+ out.println(" " + keyword + indent(name, " ") + indentedStepLocation(location));
79
+ out.flush();
80
+ }
81
+
82
+ public void flushTable() {
83
+ if (rows == null) return;
84
+ int columnCount = rows.get(0).size();
85
+ int[][] cellLengths = new int[rows.size()][columnCount];
86
+ int[] maxLengths = new int[columnCount];
87
+ for (int i = 0; i < rows.size(); i++) {
88
+ for (int j = 0; j < columnCount; j++) {
89
+ int length = rows.get(i).get(j).length();
90
+ cellLengths[i][j] = length;
91
+ maxLengths[j] = Math.max(maxLengths[j], length);
92
+ }
93
+ }
94
+
95
+ for (int i = 0; i < rows.size(); i++) {
96
+ out.write(" | ");
97
+ for (int j = 0; j < columnCount; j++) {
98
+ out.write(rows.get(i).get(j));
99
+ padSpace(maxLengths[j] - cellLengths[i][j]);
100
+ if (j < columnCount - 1) {
101
+ out.write(" | ");
102
+ } else {
103
+ out.write(" |");
104
+ }
105
+ }
106
+ out.println();
107
+ }
108
+ out.flush();
109
+ rows = null;
110
+ }
111
+
112
+ public void step(String keyword, String name, int line) {
113
+ step(keyword, name, line, null, Collections.<Argument>emptyList(), null);
114
+ }
115
+
116
+ public void row(List<String> row, int line) {
117
+ if (rows == null) rows = new ArrayList<List<String>>();
118
+ rows.add(row);
119
+ }
120
+
121
+ public void py_string(String string, int line) {
122
+ out.println(" \"\"\"");
123
+ out.print(Pattern.compile("^", Pattern.MULTILINE).matcher(string).replaceAll(" "));
124
+ out.println("\n \"\"\"");
125
+ }
126
+
127
+ public void eof() {
128
+ flushTable();
129
+ out.flush();
130
+ }
131
+
132
+ public void syntax_error(String state, String event, List<String> legalEvents, int line) {
133
+ out.println("Syntax error:" + state + ' ' + event);
134
+ }
135
+
136
+ public void steps(List<List<String>> steps) {
137
+ stepLengths = new int[steps.size()];
138
+ int i = 0;
139
+ for (List<String> step : steps) {
140
+ int stepLength = step.get(0).length() + step.get(1).length();
141
+ stepLengths[i++] = stepLength;
142
+ maxStepLength = Math.max(maxStepLength, stepLength);
143
+ }
144
+ stepIndex = -1;
145
+ }
146
+
147
+ private void padSpace(int indent, StringBuffer buffer) {
148
+ for (int i = 0; i < indent; i++) {
149
+ buffer.append(" ");
150
+ }
151
+ }
152
+
153
+ private void padSpace(int indent) {
154
+ for (int i = 0; i < indent; i++) {
155
+ out.write(" ");
156
+ }
157
+ }
158
+
159
+ private void printCommentsAndTags(String indent) {
160
+ printComments(indent);
161
+ printTags(indent);
162
+ }
163
+
164
+ private boolean printComments(String indent) {
165
+ if (comments == null) return false;
166
+ for (String comment : comments) {
167
+ out.print(indent);
168
+ out.println(comment);
169
+ }
170
+ out.flush();
171
+ comments = null;
172
+ return true;
173
+ }
174
+
175
+ private boolean printTags(String indent) {
176
+ if (tags == null) return false;
177
+ out.print(indent);
178
+ out.println(join(tags, " "));
179
+ out.flush();
180
+ tags = null;
181
+ return true;
182
+ }
183
+
184
+ private String indent(String name, String indentation) {
185
+ String indent = "";
186
+ StringBuilder sb = new StringBuilder();
187
+ String[] lines = name.split("\\n");
188
+ for (int i = 0; i < lines.length; i++) {
189
+ sb.append(indent).append(lines[i]);
190
+ if (i < lines.length - 1) {
191
+ sb.append("\n");
192
+ }
193
+ indent = indentation;
194
+ }
195
+ return sb.toString();
196
+ }
197
+
198
+ private String indentedScenarioLocation(String keyword, String name, String location) {
199
+ if (location == null || "".equals(location)) return "";
200
+ int l = keyword.length() + name.length();
201
+ maxStepLength = Math.max(maxStepLength, l);
202
+ int indent = maxStepLength - l;
203
+
204
+ StringBuffer buffer = new StringBuffer();
205
+ padSpace(indent, buffer);
206
+ buffer.append(" ").append(comments("# " + location, monochrome));
207
+ return buffer.toString();
208
+ }
209
+
210
+ private String indentedStepLocation(String location) {
211
+ if (location == null || "".equals(location)) return "";
212
+ int indent = maxStepLength - stepLengths[stepIndex += 1];
213
+
214
+ StringBuffer buffer = new StringBuffer();
215
+ padSpace(indent, buffer);
216
+ buffer.append(" ").append(comments("# " + location, monochrome));
217
+ return buffer.toString();
218
+ }
219
+ }
@@ -7,6 +7,7 @@ import gherkin.lexer.En;
7
7
 
8
8
  import java.io.IOException;
9
9
  import java.util.List;
10
+ import java.util.ArrayList;
10
11
 
11
12
  public class StateMachineReader implements Listener {
12
13
  private final String machinePath;
@@ -17,9 +18,10 @@ public class StateMachineReader implements Listener {
17
18
  }
18
19
 
19
20
  public List<List<String>> transitionTable() {
21
+ transitionTable = new ArrayList<List<String>>();
20
22
  Lexer lexer = new En(this);
21
23
  try {
22
- lexer.scan(FixJava.readResourceAsString(machinePath));
24
+ lexer.scan(FixJava.readResource(machinePath));
23
25
  } catch (IOException e) {
24
26
  throw new RuntimeException("Fatal error. Couldn't read " + machinePath);
25
27
  }
@@ -53,10 +55,13 @@ public class StateMachineReader implements Listener {
53
55
  public void py_string(String string, int line) {
54
56
  }
55
57
 
58
+ public void eof() {
59
+ }
60
+
56
61
  public void syntax_error(String name, String event, List<String> strings, int line) {
57
62
  }
58
63
 
59
- public void table(List<List<String>> rows, int line) {
60
- transitionTable = rows;
64
+ public void row(List<String> row, int line) {
65
+ transitionTable.add(row);
61
66
  }
62
67
  }