gherkin 1.0.3 → 1.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/History.txt +15 -6
  2. data/README.rdoc +23 -0
  3. data/Rakefile +1 -0
  4. data/VERSION.yml +1 -1
  5. data/features/step_definitions/gherkin_steps.rb +1 -1
  6. data/features/step_definitions/{pretty_printer_steps.rb → pretty_listener_step.rb} +2 -2
  7. data/gherkin.gemspec +24 -35
  8. data/java/Gherkin.iml +8 -14
  9. data/java/src/{gherkin → main/java/gherkin}/lexer/.gitignore +0 -0
  10. data/lib/gherkin.rb +1 -1
  11. data/lib/gherkin/c_lexer.rb +2 -2
  12. data/lib/gherkin/{format → formatter}/argument.rb +1 -1
  13. data/lib/gherkin/{tools → formatter}/colors.rb +1 -1
  14. data/lib/gherkin/{format → formatter}/monochrome_format.rb +1 -1
  15. data/lib/gherkin/{tools → formatter}/pretty_listener.rb +8 -9
  16. data/lib/gherkin/i18n.rb +27 -5
  17. data/lib/gherkin/i18n_lexer.rb +18 -44
  18. data/lib/gherkin/parser/filter_listener.rb +191 -0
  19. data/lib/gherkin/parser/parser.rb +142 -0
  20. data/lib/gherkin/parser/sexp.rb +45 -0
  21. data/lib/gherkin/parser/tag_expression.rb +46 -0
  22. data/lib/gherkin/rb_lexer.rb +2 -2
  23. data/ragel/lexer_common.rl.erb +1 -1
  24. data/spec/gherkin/{format → formatter}/argument_spec.rb +2 -2
  25. data/spec/gherkin/{tools → formatter}/colors_spec.rb +2 -2
  26. data/spec/gherkin/{tools → formatter}/pretty_listener_spec.rb +5 -5
  27. data/spec/gherkin/i18n_lexer_spec.rb +3 -3
  28. data/spec/gherkin/parser/filter_listener_spec.rb +363 -0
  29. data/spec/gherkin/parser/parser_spec.rb +35 -0
  30. data/spec/gherkin/parser/tag_expression_spec.rb +120 -0
  31. data/spec/gherkin/rb_lexer_spec.rb +0 -1
  32. data/spec/gherkin/sexp_recorder.rb +3 -3
  33. data/spec/gherkin/shared/lexer_spec.rb +19 -19
  34. data/spec/gherkin/shared/tags_spec.rb +4 -4
  35. data/tasks/bench.rake +2 -2
  36. data/tasks/compile.rake +1 -1
  37. data/tasks/ragel_task.rb +1 -1
  38. metadata +25 -36
  39. data/java/build.xml +0 -16
  40. data/java/src/gherkin/FixJava.java +0 -37
  41. data/java/src/gherkin/I18nLexer.java +0 -48
  42. data/java/src/gherkin/Lexer.java +0 -5
  43. data/java/src/gherkin/LexingError.java +0 -7
  44. data/java/src/gherkin/Listener.java +0 -29
  45. data/java/src/gherkin/Main.java +0 -17
  46. data/java/src/gherkin/ParseError.java +0 -22
  47. data/java/src/gherkin/Parser.java +0 -191
  48. data/java/src/gherkin/formatter/Argument.java +0 -39
  49. data/java/src/gherkin/formatter/ArgumentFormat.java +0 -17
  50. data/java/src/gherkin/formatter/Colors.java +0 -7
  51. data/java/src/gherkin/formatter/Formatter.java +0 -15
  52. data/java/src/gherkin/formatter/PrettyFormatter.java +0 -219
  53. data/java/src/gherkin/parser/StateMachineReader.java +0 -67
  54. data/java/test/gherkin/formatter/ArgumentTest.java +0 -17
  55. data/lib/gherkin/lexer.rb +0 -35
  56. data/lib/gherkin/parser.rb +0 -19
  57. data/lib/gherkin/rb_parser.rb +0 -125
  58. data/spec/gherkin/parser_spec.rb +0 -33
@@ -1,48 +0,0 @@
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
- }
@@ -1,5 +0,0 @@
1
- package gherkin;
2
-
3
- public interface Lexer {
4
- public void scan(CharSequence inputSequence);
5
- }
@@ -1,7 +0,0 @@
1
- package gherkin;
2
-
3
- public class LexingError extends RuntimeException {
4
- public LexingError(String message) {
5
- super(message);
6
- }
7
- }
@@ -1,29 +0,0 @@
1
- package gherkin;
2
-
3
- import java.util.List;
4
-
5
- public interface Listener {
6
- void tag(String name, int line);
7
-
8
- void comment(String content, int line);
9
-
10
- void feature(String keyword, String name, int line);
11
-
12
- void background(String keyword, String name, int line);
13
-
14
- void scenario(String keyword, String name, int line);
15
-
16
- void scenario_outline(String keyword, String name, int line);
17
-
18
- void examples(String keyword, String name, int line);
19
-
20
- void step(String keyword, String name, int line);
21
-
22
- void row(List<String> row, int line);
23
-
24
- void py_string(String string, int line);
25
-
26
- void eof();
27
-
28
- void syntax_error(String state, String event, List<String> legalEvents, int line);
29
- }
@@ -1,17 +0,0 @@
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
- }
@@ -1,22 +0,0 @@
1
- package gherkin;
2
-
3
- import java.util.List;
4
-
5
- public class ParseError extends RuntimeException {
6
- private final String state;
7
- private final List<String> expectedEvents;
8
-
9
- public ParseError(String state, String event, List<String> expectedEvents, int line) {
10
- super("Parse error on line " + line + ". Found " + event + " when expecting one of: " + FixJava.join(expectedEvents, ", ") + ". (Current state: " + state + ").");
11
- this.state = state;
12
- this.expectedEvents = expectedEvents;
13
- }
14
-
15
- public String state() {
16
- return state;
17
- }
18
-
19
- public List<String> expectedEvents() {
20
- return expectedEvents;
21
- }
22
- }
@@ -1,191 +0,0 @@
1
- package gherkin;
2
-
3
- import gherkin.parser.StateMachineReader;
4
-
5
- import java.util.*;
6
- import java.util.regex.Matcher;
7
- import java.util.regex.Pattern;
8
-
9
- public class Parser implements Listener {
10
- List<Machine> machines = new ArrayList<Machine>();
11
-
12
- private Listener listener;
13
- private boolean throwOnError;
14
-
15
- public Parser(Listener listener) {
16
- this(listener, true);
17
- }
18
-
19
- public Parser(Listener listener, boolean throwOnError) {
20
- this(listener, throwOnError, "root");
21
- }
22
-
23
- public Parser(Listener listener, boolean throwOnError, String machineName) {
24
- this.listener = listener;
25
- this.throwOnError = throwOnError;
26
- pushMachine(machineName);
27
- }
28
-
29
- private void pushMachine(String machineName) {
30
- machines.add(new Machine(this, machineName));
31
- }
32
-
33
- private void popMachine() {
34
- machines.remove(machines.size() - 1);
35
- }
36
-
37
- public void tag(String name, int line) {
38
- if (event("tag", line))
39
- listener.tag(name, line);
40
- }
41
-
42
- public void py_string(String string, int line) {
43
- if (event("py_string", line))
44
- listener.py_string(string, line);
45
- }
46
-
47
- public void feature(String keyword, String name, int line) {
48
- if (event("feature", line))
49
- listener.feature(keyword, name, line);
50
- }
51
-
52
- public void background(String keyword, String name, int line) {
53
- if (event("background", line))
54
- listener.background(keyword, name, line);
55
- }
56
-
57
- public void scenario(String keyword, String name, int line) {
58
- if (event("scenario", line))
59
- listener.scenario(keyword, name, line);
60
- }
61
-
62
- public void scenario_outline(String keyword, String name, int line) {
63
- if (event("scenario_outline", line))
64
- listener.scenario_outline(keyword, name, line);
65
- }
66
-
67
- public void examples(String keyword, String name, int line) {
68
- if (event("examples", line))
69
- listener.examples(keyword, name, line);
70
- }
71
-
72
- public void step(String keyword, String name, int line) {
73
- if (event("step", line))
74
- listener.step(keyword, name, line);
75
- }
76
-
77
- public void comment(String content, int line) {
78
- if (event("comment", line))
79
- listener.comment(content, line);
80
- }
81
-
82
- public void row(List<String> row, int line) {
83
- if (event("row", line))
84
- listener.row(row, line);
85
- }
86
-
87
- public void eof() {
88
- if (event("eof", 1))
89
- listener.eof();
90
- }
91
-
92
- public void syntax_error(String name, String event, List<String> strings, int line) {
93
- }
94
-
95
- private boolean event(String event, int line) {
96
- try {
97
- machine().event(event, line);
98
- return true;
99
- } catch (ParseError e) {
100
- if (throwOnError) {
101
- throw e;
102
- } else {
103
- listener.syntax_error(e.state(), event, e.expectedEvents(), line);
104
- return false;
105
- }
106
- }
107
- }
108
-
109
- private Machine machine() {
110
- return machines.get(machines.size() - 1);
111
- }
112
-
113
- private static class Machine {
114
- private static final Pattern PUSH = Pattern.compile("push\\((.+)\\)");
115
- private static final Map<String, Map<String, Map<String, String>>> TRANSITION_MAPS = new HashMap<String, Map<String, Map<String, String>>>();
116
-
117
- private final Parser parser;
118
- private final String name;
119
- private String state;
120
- private Map<String, Map<String, String>> transitionMap;
121
-
122
- public Machine(Parser parser, String name) {
123
- this.parser = parser;
124
- this.name = name;
125
- this.state = name;
126
- this.transitionMap = transitionMap(name);
127
- }
128
-
129
- public void event(String event, int line) {
130
- Map<String, String> states = transitionMap.get(state);
131
- if (states == null) {
132
- throw new RuntimeException("Unknown state: " + state + " for machine " + name);
133
- }
134
- String newState = states.get(event);
135
- if (newState == null) {
136
- throw new RuntimeException("Unknown transition: " + event + " among " + states + " for machine " + name + " in state " + state);
137
- }
138
- if ("E".equals(newState)) {
139
- throw new ParseError(state, event, expectedEvents(), line);
140
- } else {
141
- Matcher push = PUSH.matcher(newState);
142
- if (push.matches()) {
143
- parser.pushMachine(push.group(1));
144
- parser.event(event, line);
145
- } else if ("pop()".equals(newState)) {
146
- parser.popMachine();
147
- parser.event(event, line);
148
- } else {
149
- state = newState;
150
- }
151
- }
152
- }
153
-
154
- private List<String> expectedEvents() {
155
- List<String> result = new ArrayList<String>();
156
- for (String event : transitionMap.get(state).keySet()) {
157
- if (!transitionMap.get(state).get(event).equals("E")) {
158
- result.add(event);
159
- }
160
- }
161
- Collections.sort(result);
162
- result.remove("eof");
163
- return result;
164
- }
165
-
166
- private Map<String, Map<String, String>> transitionMap(String name) {
167
- Map<String, Map<String, String>> map = TRANSITION_MAPS.get(name);
168
- if(map == null) {
169
- map = buildTransitionMap(name);
170
- TRANSITION_MAPS.put(name, map);
171
- }
172
- return map;
173
- }
174
-
175
- private Map<String, Map<String, String>> buildTransitionMap(String name) {
176
- Map<String, Map<String, String>> result = new HashMap<String, Map<String, String>>();
177
- List<List<String>> transitionTable = new StateMachineReader(name).transitionTable();
178
- List<String> events = transitionTable.get(0).subList(1, transitionTable.get(0).size());
179
- for (List<String> actions : transitionTable.subList(1, transitionTable.size())) {
180
- Map<String, String> transitions = new HashMap<String, String>();
181
- int col = 1;
182
- for (String event : events) {
183
- transitions.put(event, actions.get(col++));
184
- }
185
- String state = actions.get(0);
186
- result.put(state, transitions);
187
- }
188
- return result;
189
- }
190
- }
191
- }
@@ -1,39 +0,0 @@
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
- }
@@ -1,17 +0,0 @@
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
- }
@@ -1,7 +0,0 @@
1
- package gherkin.formatter;
2
-
3
- public class Colors {
4
- public static String comments(String s, boolean monochrome) {
5
- return s;
6
- }
7
- }
@@ -1,15 +0,0 @@
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
- }
@@ -1,219 +0,0 @@
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
- }