bryanl-gherkin 2.11.1.1-java

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 (142) hide show
  1. data/.gitattributes +2 -0
  2. data/.mailmap +2 -0
  3. data/.rbenv-gemsets +1 -0
  4. data/.rspec +1 -0
  5. data/.rvmrc +1 -0
  6. data/.travis.yml +16 -0
  7. data/.yardopts +5 -0
  8. data/Gemfile +5 -0
  9. data/History.md +788 -0
  10. data/LICENSE +20 -0
  11. data/README.md +272 -0
  12. data/Rakefile +26 -0
  13. data/build_native_gems.sh +7 -0
  14. data/cucumber.yml +4 -0
  15. data/examples/parse_and_output_json.rb +19 -0
  16. data/features/.cucumber/stepdefs.json +244 -0
  17. data/features/escaped_pipes.feature +8 -0
  18. data/features/feature_parser.feature +237 -0
  19. data/features/json_formatter.feature +498 -0
  20. data/features/json_parser.feature +331 -0
  21. data/features/native_lexer.feature +19 -0
  22. data/features/parser_with_native_lexer.feature +205 -0
  23. data/features/pretty_formatter.feature +16 -0
  24. data/features/step_definitions/eyeball_steps.rb +3 -0
  25. data/features/step_definitions/gherkin_steps.rb +29 -0
  26. data/features/step_definitions/json_formatter_steps.rb +30 -0
  27. data/features/step_definitions/json_parser_steps.rb +20 -0
  28. data/features/step_definitions/pretty_formatter_steps.rb +85 -0
  29. data/features/steps_parser.feature +46 -0
  30. data/features/support/env.rb +42 -0
  31. data/gherkin.gemspec +77 -0
  32. data/install_mingw_os_x.sh +7 -0
  33. data/js/.npmignore +1 -0
  34. data/js/lib/gherkin/lexer/.npmignore +0 -0
  35. data/lib/gherkin/c_lexer.rb +17 -0
  36. data/lib/gherkin/formatter/ansi_escapes.rb +97 -0
  37. data/lib/gherkin/formatter/argument.rb +16 -0
  38. data/lib/gherkin/formatter/escaping.rb +15 -0
  39. data/lib/gherkin/formatter/filter_formatter.rb +146 -0
  40. data/lib/gherkin/formatter/hashable.rb +19 -0
  41. data/lib/gherkin/formatter/json_formatter.rb +122 -0
  42. data/lib/gherkin/formatter/line_filter.rb +26 -0
  43. data/lib/gherkin/formatter/model.rb +281 -0
  44. data/lib/gherkin/formatter/pretty_formatter.rb +244 -0
  45. data/lib/gherkin/formatter/regexp_filter.rb +21 -0
  46. data/lib/gherkin/formatter/step_printer.rb +21 -0
  47. data/lib/gherkin/formatter/tag_count_formatter.rb +47 -0
  48. data/lib/gherkin/formatter/tag_filter.rb +19 -0
  49. data/lib/gherkin/i18n.rb +180 -0
  50. data/lib/gherkin/i18n.yml +613 -0
  51. data/lib/gherkin/js_lexer.rb +20 -0
  52. data/lib/gherkin/json_parser.rb +177 -0
  53. data/lib/gherkin/lexer/i18n_lexer.rb +46 -0
  54. data/lib/gherkin/listener/event.rb +45 -0
  55. data/lib/gherkin/listener/formatter_listener.rb +143 -0
  56. data/lib/gherkin/native/java.rb +72 -0
  57. data/lib/gherkin/native/null.rb +5 -0
  58. data/lib/gherkin/native/therubyracer.rb +39 -0
  59. data/lib/gherkin/native.rb +7 -0
  60. data/lib/gherkin/parser/meta.txt +5 -0
  61. data/lib/gherkin/parser/parser.rb +164 -0
  62. data/lib/gherkin/parser/root.txt +11 -0
  63. data/lib/gherkin/parser/steps.txt +4 -0
  64. data/lib/gherkin/rb_lexer/README.rdoc +8 -0
  65. data/lib/gherkin/rb_lexer.rb +8 -0
  66. data/lib/gherkin/rubify.rb +24 -0
  67. data/lib/gherkin/tag_expression.rb +62 -0
  68. data/lib/gherkin.jar +0 -0
  69. data/lib/gherkin.rb +2 -0
  70. data/ragel/lexer.c.rl.erb +454 -0
  71. data/ragel/lexer.java.rl.erb +219 -0
  72. data/ragel/lexer.js.rl.erb +227 -0
  73. data/ragel/lexer.rb.rl.erb +174 -0
  74. data/ragel/lexer_common.rl.erb +50 -0
  75. data/spec/gherkin/c_lexer_spec.rb +22 -0
  76. data/spec/gherkin/fixtures/1.feature +8 -0
  77. data/spec/gherkin/fixtures/comments_in_table.feature +9 -0
  78. data/spec/gherkin/fixtures/complex.feature +45 -0
  79. data/spec/gherkin/fixtures/complex.json +139 -0
  80. data/spec/gherkin/fixtures/complex_for_filtering.feature +60 -0
  81. data/spec/gherkin/fixtures/complex_with_tags.feature +61 -0
  82. data/spec/gherkin/fixtures/dos_line_endings.feature +45 -0
  83. data/spec/gherkin/fixtures/examples_with_only_header.feature +14 -0
  84. data/spec/gherkin/fixtures/hantu_pisang.feature +35 -0
  85. data/spec/gherkin/fixtures/i18n_fr.feature +14 -0
  86. data/spec/gherkin/fixtures/i18n_fr2.feature +8 -0
  87. data/spec/gherkin/fixtures/i18n_no.feature +7 -0
  88. data/spec/gherkin/fixtures/i18n_pt1.feature +44 -0
  89. data/spec/gherkin/fixtures/i18n_pt2.feature +4 -0
  90. data/spec/gherkin/fixtures/i18n_pt3.feature +4 -0
  91. data/spec/gherkin/fixtures/i18n_pt4.feature +4 -0
  92. data/spec/gherkin/fixtures/i18n_zh-CN.feature +9 -0
  93. data/spec/gherkin/fixtures/issue_145.feature +22 -0
  94. data/spec/gherkin/fixtures/scenario_outline_with_tags.feature +13 -0
  95. data/spec/gherkin/fixtures/scenario_without_steps.feature +5 -0
  96. data/spec/gherkin/fixtures/simple_with_comments.feature +7 -0
  97. data/spec/gherkin/fixtures/simple_with_tags.feature +11 -0
  98. data/spec/gherkin/fixtures/with_bom.feature +3 -0
  99. data/spec/gherkin/formatter/ansi_escapes_spec.rb +32 -0
  100. data/spec/gherkin/formatter/filter_formatter_spec.rb +204 -0
  101. data/spec/gherkin/formatter/json_formatter_spec.rb +92 -0
  102. data/spec/gherkin/formatter/model_spec.rb +28 -0
  103. data/spec/gherkin/formatter/pretty_formatter_spec.rb +177 -0
  104. data/spec/gherkin/formatter/spaces.feature +9 -0
  105. data/spec/gherkin/formatter/step_printer_spec.rb +55 -0
  106. data/spec/gherkin/formatter/tabs.feature +9 -0
  107. data/spec/gherkin/formatter/tag_count_formatter_spec.rb +30 -0
  108. data/spec/gherkin/i18n_spec.rb +241 -0
  109. data/spec/gherkin/java_lexer_spec.rb +20 -0
  110. data/spec/gherkin/js_lexer_spec.rb +23 -0
  111. data/spec/gherkin/json_parser_spec.rb +176 -0
  112. data/spec/gherkin/lexer/i18n_lexer_spec.rb +43 -0
  113. data/spec/gherkin/output_stream_string_io.rb +20 -0
  114. data/spec/gherkin/parser/parser_spec.rb +16 -0
  115. data/spec/gherkin/rb_lexer_spec.rb +20 -0
  116. data/spec/gherkin/sexp_recorder.rb +59 -0
  117. data/spec/gherkin/shared/bom_group.rb +20 -0
  118. data/spec/gherkin/shared/doc_string_group.rb +163 -0
  119. data/spec/gherkin/shared/lexer_group.rb +591 -0
  120. data/spec/gherkin/shared/row_group.rb +125 -0
  121. data/spec/gherkin/shared/tags_group.rb +54 -0
  122. data/spec/gherkin/tag_expression_spec.rb +142 -0
  123. data/spec/spec_helper.rb +75 -0
  124. data/tasks/bench/feature_builder.rb +49 -0
  125. data/tasks/bench/null_listener.rb +4 -0
  126. data/tasks/bench.rake +184 -0
  127. data/tasks/compile.rake +120 -0
  128. data/tasks/cucumber.rake +22 -0
  129. data/tasks/gems.rake +31 -0
  130. data/tasks/ikvm.rake +124 -0
  131. data/tasks/ragel_task.rb +100 -0
  132. data/tasks/release.rake +49 -0
  133. data/tasks/rspec.rake +8 -0
  134. data/tasks/yard/default/layout/html/bubble_32x32.png +0 -0
  135. data/tasks/yard/default/layout/html/bubble_48x48.png +0 -0
  136. data/tasks/yard/default/layout/html/footer.erb +5 -0
  137. data/tasks/yard/default/layout/html/index.erb +1 -0
  138. data/tasks/yard/default/layout/html/layout.erb +25 -0
  139. data/tasks/yard/default/layout/html/logo.erb +1 -0
  140. data/tasks/yard/default/layout/html/setup.rb +4 -0
  141. data/tasks/yard.rake +7 -0
  142. metadata +412 -0
@@ -0,0 +1,177 @@
1
+ require 'json'
2
+ require 'gherkin/formatter/model'
3
+ require 'gherkin/formatter/argument'
4
+ require 'gherkin/native'
5
+ require 'base64'
6
+
7
+ module Gherkin
8
+ class JSONParser
9
+ native_impl('gherkin')
10
+
11
+ include Base64
12
+
13
+ def initialize(reporter, formatter)
14
+ @reporter, @formatter = reporter, formatter
15
+ end
16
+
17
+ # Parse a gherkin object +o+, which can either be a JSON String,
18
+ # or a Hash (from a parsed JSON String).
19
+ def parse(o)
20
+ o = JSON.parse(o) if String === o
21
+
22
+ o.each do |f|
23
+ @formatter.uri(f['uri'])
24
+ Formatter::Model::Feature.new(comments(f), tags(f), keyword(f), name(f), description(f), line(f), id(f)).replay(@formatter)
25
+ (f["elements"] || []).each do |feature_element|
26
+ feature_element(feature_element).replay(@formatter)
27
+
28
+ (feature_element["before"] || []).each do |hook|
29
+ before(hook)
30
+ end
31
+
32
+ (feature_element["steps"] || []).each do |step|
33
+ step(step).replay(@formatter)
34
+ match(step)
35
+ result(step)
36
+ embeddings(step)
37
+ output(step)
38
+ end
39
+
40
+ (feature_element["after"] || []).each do |hook|
41
+ after(hook)
42
+ end
43
+
44
+ (feature_element["examples"] || []).each do |eo|
45
+ Formatter::Model::Examples.new(comments(eo), tags(eo), keyword(eo), name(eo), description(eo), line(eo), id(eo), examples_rows(eo['rows'])).replay(@formatter)
46
+ end
47
+ end
48
+ end
49
+
50
+ @formatter.eof
51
+ end
52
+
53
+ private
54
+
55
+ def feature_element(o)
56
+ case o['type']
57
+ when 'background'
58
+ Formatter::Model::Background.new(comments(o), keyword(o), name(o), description(o), line(o))
59
+ when 'scenario'
60
+ Formatter::Model::Scenario.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o), id(o))
61
+ when 'scenario_outline'
62
+ Formatter::Model::ScenarioOutline.new(comments(o), tags(o), keyword(o), name(o), description(o), line(o), id(o))
63
+ end
64
+ end
65
+
66
+ def step(o)
67
+ builder = Formatter::Model::Step::Builder.new(comments(o), keyword(o), name(o), line(o))
68
+
69
+ (o['rows'] || []).each do |row|
70
+ builder.row comments(row), row['cells'], row['line'], nil
71
+ end
72
+
73
+ if(o['doc_string'])
74
+ ds = o['doc_string']
75
+ builder.doc_string ds['value'], ds['content_type'].to_s, ds['line']
76
+ end
77
+
78
+ builder.build
79
+ end
80
+
81
+ def match(o)
82
+ if(m = o['match'])
83
+ Formatter::Model::Match.new(arguments(m), location(m)).replay(@reporter)
84
+ end
85
+ end
86
+
87
+ def result(o)
88
+ if(r = o['result'])
89
+ Formatter::Model::Result.new(status(r), duration(r), error_message(r)).replay(@reporter)
90
+ end
91
+ end
92
+
93
+ def before(o)
94
+ m = o['match']
95
+ match = Formatter::Model::Match.new([], location(m))
96
+ r = o['result']
97
+ result = Formatter::Model::Result.new(status(r), duration(r), error_message(r))
98
+ @reporter.before(match, result)
99
+ end
100
+
101
+ def after(o)
102
+ m = o['match']
103
+ match = Formatter::Model::Match.new([], location(m))
104
+ r = o['result']
105
+ result = Formatter::Model::Result.new(status(r), duration(r), error_message(r))
106
+ @reporter.after(match, result)
107
+ end
108
+
109
+ def embeddings(o)
110
+ (o['embeddings'] || []).each do |embedding|
111
+ @reporter.embedding(embedding['mime_type'], Base64::decode64(embedding['data']))
112
+ end
113
+ end
114
+
115
+ def output(o)
116
+ (o['output'] || []).each do |text|
117
+ @reporter.write(text)
118
+ end
119
+ end
120
+
121
+ def examples_rows(o)
122
+ o.map{|row| Formatter::Model::ExamplesTableRow.new(comments(row), row['cells'], row['line'], row['id'])}
123
+ end
124
+
125
+ def comments(o)
126
+ (o['comments'] || []).map do |comment|
127
+ Formatter::Model::Comment.new(comment['value'], comment['line'])
128
+ end
129
+ end
130
+
131
+ def tags(o)
132
+ (o['tags'] || []).map do |tag|
133
+ Formatter::Model::Tag.new(tag['name'], tag['line'])
134
+ end
135
+ end
136
+
137
+ def keyword(o)
138
+ o['keyword']
139
+ end
140
+
141
+ def name(o)
142
+ o['name']
143
+ end
144
+
145
+ def description(o)
146
+ o['description']
147
+ end
148
+
149
+ def line(o)
150
+ o['line']
151
+ end
152
+
153
+ def id(o)
154
+ o['id']
155
+ end
156
+
157
+ def arguments(m)
158
+ m['arguments'].map{|a| Formatter::Argument.new(a['offset'], a['val'])}
159
+ end
160
+
161
+ def location(m)
162
+ m['location']
163
+ end
164
+
165
+ def status(r)
166
+ r['status']
167
+ end
168
+
169
+ def duration(r)
170
+ r['duration']
171
+ end
172
+
173
+ def error_message(r)
174
+ r['error_message']
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,46 @@
1
+ require 'gherkin/i18n'
2
+ require 'gherkin/native'
3
+
4
+ module Gherkin
5
+ module Lexer
6
+ LexingError = Class.new(StandardError)
7
+
8
+ # The main entry point to lexing Gherkin source.
9
+ class I18nLexer
10
+ native_impl('gherkin')
11
+
12
+ COMMENT_OR_EMPTY_LINE_PATTERN = /^\s*#|^\s*$/
13
+ LANGUAGE_PATTERN = /^\s*#\s*language\s*:\s*([a-zA-Z\-]+)/ #:nodoc:
14
+ attr_reader :i18n_language
15
+
16
+ def initialize(listener, force_ruby=false)
17
+ @listener = listener
18
+ @force_ruby = force_ruby
19
+ end
20
+
21
+ def scan(source)
22
+ create_delegate(source).scan(source)
23
+ end
24
+
25
+ private
26
+
27
+ def create_delegate(source)
28
+ @i18n_language = lang(source)
29
+ @i18n_language.lexer(@listener, @force_ruby)
30
+ end
31
+
32
+ def lang(source)
33
+ key = 'en'
34
+ source.each_line do |line|
35
+ break unless COMMENT_OR_EMPTY_LINE_PATTERN =~ line
36
+ if LANGUAGE_PATTERN =~ line
37
+ key = $1
38
+ break
39
+ end
40
+ end
41
+ I18n.get(key)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,45 @@
1
+ module Gherkin
2
+ module Listener
3
+ class Event < Array
4
+ def event
5
+ self[0]
6
+ end
7
+
8
+ def keyword
9
+ self[1]
10
+ end
11
+
12
+ def line_match?(lines)
13
+ lines.include?(line)
14
+ end
15
+
16
+ def name_match?(name_regexen)
17
+ return false unless [:feature, :background, :scenario, :scenario_outline, :examples].include?(event)
18
+ name_regexen.detect{|name_regex| name =~ name_regex}
19
+ end
20
+
21
+ def replay(listener)
22
+ begin
23
+ listener.__send__(event, *args)
24
+ rescue ArgumentError => e
25
+ e.message << "\nListener: #{listener.class}, args: #{args.inspect}"
26
+ raise e
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def name
33
+ self[2]
34
+ end
35
+
36
+ def line
37
+ self[-1]
38
+ end
39
+
40
+ def args
41
+ self[1..-1]
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,143 @@
1
+ require 'gherkin/native'
2
+ require 'gherkin/formatter/model'
3
+
4
+ module Gherkin
5
+ module Listener
6
+ # Adapter from the "raw" Gherkin <tt>Listener</tt> API
7
+ # to the slightly more high-level <tt>Formatter</tt> API,
8
+ # which is easier to implement (less state to keep track of).
9
+ class FormatterListener
10
+ native_impl('gherkin')
11
+
12
+ def initialize(formatter)
13
+ @formatter = formatter
14
+ @stash = Stash.new
15
+ end
16
+
17
+ def comment(value, line)
18
+ @stash.comment Formatter::Model::Comment.new(value, line)
19
+ end
20
+
21
+ def tag(name, line)
22
+ @stash.tag Formatter::Model::Tag.new(name, line)
23
+ end
24
+
25
+ def feature(keyword, name, description, line)
26
+ @stash.feature(name) do |comments, tags, id|
27
+ replay Formatter::Model::Feature.new(comments, tags, keyword, name, description, line, id)
28
+ end
29
+ end
30
+
31
+ def background(keyword, name, description, line)
32
+ @stash.feature_element(name) do |comments, tags, id|
33
+ replay Formatter::Model::Background.new(comments, keyword, name, description, line)
34
+ end
35
+ end
36
+
37
+ def scenario(keyword, name, description, line)
38
+ replay_step_or_examples
39
+ @stash.feature_element(name) do |comments, tags, id|
40
+ replay Formatter::Model::Scenario.new(comments, tags, keyword, name, description, line, id)
41
+ end
42
+ end
43
+
44
+ def scenario_outline(keyword, name, description, line)
45
+ replay_step_or_examples
46
+ @stash.feature_element(name) do |comments, tags, id|
47
+ replay Formatter::Model::ScenarioOutline.new(comments, tags, keyword, name, description, line, id)
48
+ end
49
+ end
50
+
51
+ def examples(keyword, name, description, line)
52
+ replay_step_or_examples
53
+ @stash.examples(name) do |comments, tags, id|
54
+ @current_builder = Formatter::Model::Examples::Builder.new(comments, tags, keyword, name, description, line, id)
55
+ end
56
+ end
57
+
58
+ def step(keyword, name, line)
59
+ replay_step_or_examples
60
+ @stash.basic_statement do |comments, id|
61
+ @current_builder = Formatter::Model::Step::Builder.new(comments, keyword, name, line)
62
+ end
63
+ end
64
+
65
+ def row(cells, line)
66
+ @stash.basic_statement do |comments, id|
67
+ @current_builder.row(comments, cells, line, id)
68
+ end
69
+ end
70
+
71
+ def doc_string(content_type, value, line)
72
+ @current_builder.doc_string(value, content_type, line)
73
+ end
74
+
75
+ def eof
76
+ replay_step_or_examples
77
+ @formatter.eof
78
+ end
79
+
80
+ def syntax_error(state, ev, legal_events, uri, line)
81
+ @formatter.syntax_error(state, ev, legal_events, uri, line)
82
+ end
83
+
84
+ private
85
+
86
+ def replay(element)
87
+ element.replay(@formatter)
88
+ end
89
+
90
+ class Stash
91
+ attr_reader :comments, :tags, :ids
92
+
93
+ def initialize
94
+ @comments, @tags, @ids = [], [], []
95
+ @row_index = 0
96
+ end
97
+
98
+ def comment(comment)
99
+ @comments << comment
100
+ end
101
+
102
+ def feature(name)
103
+ @feature_id = id(name)
104
+ yield @comments, @tags, @feature_id
105
+ @comments, @tags = [], []
106
+ end
107
+
108
+ def feature_element(name)
109
+ @feature_element_id = "#{@feature_id};#{id(name)}"
110
+ yield @comments, @tags, @feature_element_id
111
+ @comments, @tags = [], []
112
+ end
113
+
114
+ def examples(name)
115
+ @examples_id = "#{@feature_element_id};#{id(name)}"
116
+ @row_index = 0
117
+ yield @comments, @tags, @examples_id
118
+ @comments, @tags = [], []
119
+ end
120
+
121
+ def basic_statement
122
+ @row_index += 1
123
+ yield @comments, "#{@examples_id};#{@row_index}"
124
+ @comments = []
125
+ end
126
+
127
+ def tag(tag)
128
+ @tags << tag
129
+ end
130
+
131
+ def id(name)
132
+ (name || '').gsub(/[\s_]/, '-').downcase
133
+ end
134
+ end
135
+
136
+ def replay_step_or_examples
137
+ return unless @current_builder
138
+ replay(@current_builder)
139
+ @current_builder = nil
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,72 @@
1
+ class Class
2
+ class IOWriter < Java.java.io.Writer
3
+ def initialize(io)
4
+ @io = io
5
+ end
6
+
7
+ def write(cbuf, off, len)
8
+ @io.write(cbuf.unpack("U*")[off..len].pack("U*"))
9
+ end
10
+
11
+ def flush
12
+ @io.flush
13
+ end
14
+
15
+ def close
16
+ @io.close
17
+ end
18
+ end
19
+
20
+ # Causes a Java class to be instantiated instead of the Ruby class when
21
+ # running on JRuby. This is used to test both pure Java and pure Ruby classes
22
+ # from the same Ruby based test suite. The Java Class must have a package name
23
+ # that corresponds with the Ruby class.
24
+ def native_impl(lib)
25
+ require "#{lib}.jar"
26
+
27
+ class << self
28
+ def new(*args)
29
+ begin
30
+ java_class.new(*javaify(args))
31
+ rescue ArgumentError => e
32
+ e.message << "\n#{java_class.name}"
33
+ raise e
34
+ rescue NameError => e
35
+ e.message << "\n args: #{args.inspect}"
36
+ raise e
37
+ end
38
+ end
39
+
40
+ def javaify(arg)
41
+ if Array === arg
42
+ arg.map{|a| javaify(a)}
43
+ else
44
+ case(arg)
45
+ when Regexp
46
+ java.util.regex.Pattern.compile(arg.source)
47
+ when Symbol
48
+ arg.to_s
49
+ when IO
50
+ IOWriter.new(arg)
51
+ else
52
+ arg
53
+ end
54
+ end
55
+ end
56
+
57
+ def ===(object)
58
+ super || object.java_kind_of?(java_class)
59
+ end
60
+
61
+ def java_class
62
+ names = self.name.split('::')
63
+ package = Java
64
+ names[0..-2].each do |module_name|
65
+ package = package.__send__(module_name.downcase)
66
+ end
67
+
68
+ package.__send__(names[-1])
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,5 @@
1
+ class Class
2
+ def native_impl(lib)
3
+ # no-op
4
+ end
5
+ end
@@ -0,0 +1,39 @@
1
+ require 'v8'
2
+
3
+ class Class
4
+ def native_impl(lib)
5
+ class << self
6
+ def new(*args)
7
+ js = {
8
+ 'Gherkin::Formatter::JSONFormatter' => 'js/lib/gherkin/formatter/json_formatter.js'
9
+ }[self.name]
10
+ if(js)
11
+ Proxy.new(js, *args)
12
+ else
13
+ super(*args)
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+ class Proxy
20
+ def initialize(js, *args)
21
+ cxt = V8::Context.new
22
+ cxt['module'] = {}
23
+
24
+ # Mimic Node.js / Firebug console.log
25
+ cxt['console'] = STDOUT
26
+ def STDOUT.log(*a)
27
+ puts sprintf(*a)
28
+ end
29
+
30
+ cxt.load(js)
31
+ @js_obj = cxt['module']['exports'].new(*args)
32
+ end
33
+
34
+ def method_missing(name, *args)
35
+ a = args.map{|a| a.respond_to?(:to_hash) ? a.to_hash : a}
36
+ @js_obj.__send__(name, *a)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,7 @@
1
+ if defined?(JRUBY_VERSION)
2
+ require 'gherkin/native/java'
3
+ elsif ENV['GHERKIN_JS_NATIVE']
4
+ require 'gherkin/native/therubyracer'
5
+ else
6
+ require 'gherkin/native/null'
7
+ end
@@ -0,0 +1,5 @@
1
+ | | feature | background | scenario | scenario_outline | examples | step | row | doc_string | eof | comment | tag |
2
+ | meta | E | E | E | E | E | E | E | E | eof | comment | tag |
3
+ | comment | pop() | pop() | pop() | pop() | pop() | pop() | pop() | pop() | eof | pop() | tag |
4
+ | tag | pop() | E | pop() | pop() | pop() | E | E | E | E | E | tag |
5
+ | eof | E | E | E | E | E | E | E | E | E | E | E |
@@ -0,0 +1,164 @@
1
+ require 'gherkin/i18n'
2
+ require 'gherkin/lexer/i18n_lexer'
3
+ require 'gherkin/native'
4
+ require 'gherkin/listener/formatter_listener'
5
+
6
+ module Gherkin
7
+ module Parser
8
+ class ParseError < StandardError
9
+ def initialize(state, new_state, expected_states, uri, line)
10
+ super("Parse error at #{uri}:#{line}. Found #{new_state} when expecting one of: #{expected_states.join(', ')}. (Current state: #{state}).")
11
+ end
12
+ end
13
+
14
+ class Parser
15
+ native_impl('gherkin')
16
+
17
+ # Initialize the parser. +machine_name+ refers to a state machine table.
18
+ def initialize(formatter, raise_on_error=true, machine_name='root', force_ruby=false)
19
+ @formatter = formatter
20
+ @listener = Listener::FormatterListener.new(@formatter)
21
+ @raise_on_error = raise_on_error
22
+ @machine_name = machine_name
23
+ @machines = []
24
+ push_machine(@machine_name)
25
+ @lexer = Gherkin::Lexer::I18nLexer.new(self, force_ruby)
26
+ end
27
+
28
+ def parse(gherkin, feature_uri, line_offset)
29
+ @formatter.uri(feature_uri)
30
+ @line_offset = line_offset
31
+ @lexer.scan(gherkin)
32
+ end
33
+
34
+ def i18n_language
35
+ @lexer.i18n_language
36
+ end
37
+
38
+ def errors
39
+ @lexer.errors
40
+ end
41
+
42
+ # Doesn't yet fall back to super
43
+ def method_missing(method, *args)
44
+ # TODO: Catch exception and call super
45
+ event(method.to_s, args[-1])
46
+ @listener.__send__(method, *args)
47
+ if method == :eof
48
+ pop_machine
49
+ push_machine(@machine_name)
50
+ end
51
+ end
52
+
53
+ def event(ev, line)
54
+ l = line ? @line_offset+line : nil
55
+ machine.event(ev, l) do |state, legal_events|
56
+ if @raise_on_error
57
+ raise ParseError.new(state, ev, legal_events, @feature_uri, l)
58
+ else
59
+ # Only used for testing
60
+ @listener.syntax_error(state, ev, legal_events, @feature_uri, l)
61
+ end
62
+ end
63
+ end
64
+
65
+ def push_machine(name)
66
+ @machines.push(Machine.new(self, name))
67
+ end
68
+
69
+ def pop_machine
70
+ @machines.pop
71
+ end
72
+
73
+ def machine
74
+ @machines[-1]
75
+ end
76
+
77
+ def expected
78
+ machine.expected
79
+ end
80
+
81
+ def force_state(state)
82
+ machine.instance_variable_set('@state', state)
83
+ end
84
+
85
+ class Machine
86
+ def initialize(parser, name)
87
+ @parser = parser
88
+ @name = name
89
+ @transition_map = transition_map(name)
90
+ @state = name
91
+ end
92
+
93
+ def event(ev, line)
94
+ states = @transition_map[@state]
95
+ raise "Unknown state: #{@state.inspect} for machine #{@name}" if states.nil?
96
+ new_state = states[ev]
97
+ case new_state
98
+ when "E"
99
+ yield @state, expected
100
+ when /push\((.+)\)/
101
+ @parser.push_machine($1)
102
+ @parser.event(ev, line)
103
+ when "pop()"
104
+ @parser.pop_machine()
105
+ @parser.event(ev, line)
106
+ else
107
+ raise "Unknown transition: #{ev.inspect} among #{states.inspect} for machine #{@name}" if new_state.nil?
108
+ @state = new_state
109
+ end
110
+ end
111
+
112
+ def expected
113
+ allowed = @transition_map[@state].find_all { |_, action| action != "E" }
114
+ allowed.collect { |state| state[0] }.sort - ['eof']
115
+ end
116
+
117
+ private
118
+
119
+ @@transition_maps = {}
120
+
121
+ def transition_map(name)
122
+ @@transition_maps[name] ||= build_transition_map(name)
123
+ end
124
+
125
+ def build_transition_map(name)
126
+ table = transition_table(name)
127
+ events = table.shift[1..-1]
128
+ table.inject({}) do |machine, actions|
129
+ state = actions.shift
130
+ machine[state] = Hash[*events.zip(actions).flatten]
131
+ machine
132
+ end
133
+ end
134
+
135
+ def transition_table(name)
136
+ state_machine_reader = StateMachineReader.new
137
+ lexer = Gherkin::I18n.new('en').lexer(state_machine_reader)
138
+ machine = File.dirname(__FILE__) + "/#{name}.txt"
139
+ lexer.scan(File.read(machine))
140
+ state_machine_reader.rows
141
+ end
142
+
143
+ class StateMachineReader
144
+ attr_reader :rows
145
+
146
+ def initialize
147
+ @rows = []
148
+ end
149
+
150
+ def uri(uri)
151
+ end
152
+
153
+ def row(row, line_number)
154
+ @rows << row
155
+ end
156
+
157
+ def eof
158
+ end
159
+ end
160
+
161
+ end
162
+ end
163
+ end
164
+ end