gherkin-ruby 0.0.2 → 0.1.0
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.
- data/.gitignore +1 -0
- data/Rakefile +19 -1
- data/Readme.md +2 -2
- data/gherkin-ruby.gemspec +2 -1
- data/lib/gherkin/ast.rb +18 -17
- data/lib/gherkin/parser/gherkin.rex +44 -0
- data/lib/gherkin/parser/gherkin.y +100 -0
- data/lib/gherkin/parser/lexer.rb +120 -0
- data/lib/gherkin/parser/parser.rb +359 -0
- data/lib/gherkin/parser.rb +1 -43
- data/lib/gherkin/version.rb +1 -1
- data/lib/gherkin.rb +2 -7
- data/test/gherkin/ast_test.rb +19 -32
- data/test/gherkin/parser/lexer_test.rb +43 -0
- data/test/gherkin/parser/parser_test.rb +96 -0
- data/test/gherkin/parser_test.rb +52 -148
- metadata +85 -40
- data/lib/gherkin/transform.rb +0 -50
- data/test/gherkin/transform_test.rb +0 -74
data/test/gherkin/parser_test.rb
CHANGED
@@ -1,169 +1,73 @@
|
|
1
1
|
require_relative '../test_helper'
|
2
|
-
require 'parslet'
|
3
|
-
|
4
|
-
def p(rule, input, tag=nil)
|
5
|
-
tag ||= rule
|
6
|
-
parser = Gherkin::Parser.new
|
7
|
-
parser.send(rule).parse(input)[tag]
|
8
|
-
end
|
9
2
|
|
10
3
|
module Gherkin
|
11
4
|
describe 'Feature parsing' do
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
it 'parses the Scenario name' do
|
17
|
-
p(:scenario_line, " Scenario: Formal greeting", :name).must_equal "Formal greeting"
|
18
|
-
end
|
19
|
-
|
20
|
-
describe 'Steps' do
|
21
|
-
it 'parses a Given step' do
|
22
|
-
step = " Given I have an empty array"
|
23
|
-
p(:step, step, :name).must_equal "I have an empty array"
|
24
|
-
p(:step, step, :keyword).must_equal "Given"
|
25
|
-
end
|
26
|
-
|
27
|
-
it 'parses a When step' do
|
28
|
-
step = " When I have an empty array"
|
29
|
-
p(:step, step, :name).must_equal "I have an empty array"
|
30
|
-
p(:step, step, :keyword).must_equal "When"
|
31
|
-
end
|
32
|
-
|
33
|
-
it 'parses a Then step' do
|
34
|
-
step = " Then I have an empty array"
|
35
|
-
p(:step, step, :name).must_equal "I have an empty array"
|
36
|
-
p(:step, step, :keyword).must_equal "Then"
|
37
|
-
end
|
38
|
-
|
39
|
-
it 'parses an And step' do
|
40
|
-
step = " And I have an empty array"
|
41
|
-
p(:step, step, :name).must_equal "I have an empty array"
|
42
|
-
p(:step, step, :keyword).must_equal "And"
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'parses a But step' do
|
46
|
-
step = " But I have an empty array"
|
47
|
-
p(:step, step, :name).must_equal "I have an empty array"
|
48
|
-
p(:step, step, :keyword).must_equal "But"
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
describe 'Comment parsing' do
|
54
|
-
it 'parses a comment ignoring its content' do
|
55
|
-
p(:comment, "# My comment").size.must_be :>, 0
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
describe 'Description parsing' do
|
60
|
-
it 'parses multiline descriptions' do
|
61
|
-
description = """ In order to know what the heck is Gherkin
|
5
|
+
before do
|
6
|
+
@scenario = """Feature: My Feature
|
7
|
+
In order to do something #w000t peoeple
|
62
8
|
As a developer
|
63
|
-
I want
|
64
|
-
"""
|
65
|
-
parser = Gherkin::Parser.new
|
66
|
-
parser.description.parse(description)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe 'Tags parsing' do
|
71
|
-
it 'parses many tags' do
|
72
|
-
javascript = p(:tags, " @javascript @wip", 0)
|
73
|
-
wip = p(:tags, " @javascript @wip", 1)
|
74
|
-
javascript[:tag].must_equal 'javascript'
|
75
|
-
wip[:tag].must_equal 'wip'
|
76
|
-
end
|
9
|
+
I want to be happy #yeah
|
77
10
|
|
78
|
-
|
79
|
-
p(:tags, " @javascript", 0)[:tag].must_equal 'javascript'
|
80
|
-
end
|
81
|
-
end
|
11
|
+
# Attend people. This is going to be fun
|
82
12
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
steps.first[:step][:name].must_equal 'I do something'
|
88
|
-
steps.last[:step][:name].must_equal 'blah'
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
describe 'Parses scenario objects' do
|
93
|
-
it 'parses a Scenario' do
|
94
|
-
parser = Gherkin::Parser.new
|
95
|
-
scenario = " Scenario: Parse a scenario\n Given something happens\n Then something cooler happens"
|
96
|
-
result = parser.scenario.parse(scenario)
|
97
|
-
|
98
|
-
result[:name].must_equal 'Parse a scenario'
|
99
|
-
result[:steps][0][:step][:name].must_equal 'something happens'
|
100
|
-
result[:steps][1][:step][:name].must_equal 'something cooler happens'
|
101
|
-
end
|
102
|
-
|
103
|
-
it 'parses a Scenario with a tag' do
|
104
|
-
parser = Gherkin::Parser.new
|
105
|
-
scenario = " @javascript\n Scenario: Parse a scenario\n Given something happens\n Then something cooler happens"
|
106
|
-
result = parser.scenario.parse(scenario)
|
107
|
-
|
108
|
-
result[:tags].first[:tag].must_equal 'javascript'
|
109
|
-
result[:name].must_equal 'Parse a scenario'
|
110
|
-
result[:steps][0][:step][:name].must_equal 'something happens'
|
111
|
-
result[:steps][1][:step][:name].must_equal 'something cooler happens'
|
112
|
-
end
|
113
|
-
end
|
13
|
+
Background:
|
14
|
+
Given something happens before anything else happens
|
15
|
+
And more things happens before anything else happens
|
16
|
+
# And I wipe the hard drive
|
114
17
|
|
115
|
-
|
116
|
-
it 'parses a Feature' do
|
117
|
-
parser = Gherkin::Parser.new
|
118
|
-
scenario = """Feature: My Feature
|
119
|
-
@javascript @wip
|
120
|
-
Scenario: something happens
|
18
|
+
Scenario: something happens # yeah
|
121
19
|
Given something happens
|
122
20
|
Then something cooler happens
|
123
21
|
|
22
|
+
@javascript @wip #@destroy
|
124
23
|
Scenario: something else happens
|
125
24
|
Given foo
|
126
25
|
Then bar
|
127
26
|
"""
|
128
|
-
result = parser.parse(scenario)
|
129
27
|
|
130
|
-
|
131
|
-
|
132
|
-
result[:feature][:scenarios][0][:scenario][:tags].first[:tag].must_equal 'javascript'
|
133
|
-
result[:feature][:scenarios][0][:scenario][:tags].last[:tag].must_equal 'wip'
|
134
|
-
result[:feature][:scenarios][0][:scenario][:name].must_equal 'something happens'
|
135
|
-
result[:feature][:scenarios][0][:scenario][:steps][0][:step][:name].must_equal 'something happens'
|
136
|
-
result[:feature][:scenarios][0][:scenario][:steps][1][:step][:name].must_equal 'something cooler happens'
|
137
|
-
|
138
|
-
result[:feature][:scenarios][1][:scenario][:name].must_equal 'something else happens'
|
139
|
-
result[:feature][:scenarios][1][:scenario][:steps][0][:step][:name].must_equal 'foo'
|
140
|
-
result[:feature][:scenarios][1][:scenario][:steps][1][:step][:name].must_equal 'bar'
|
28
|
+
parser = Gherkin::Parser.new
|
29
|
+
@result = parser.parse(@scenario)
|
141
30
|
end
|
142
|
-
end
|
143
|
-
|
144
|
-
describe 'Parses feature objects with background' do
|
145
|
-
it 'parses a Feature' do
|
146
|
-
parser = Gherkin::Parser.new
|
147
|
-
scenario = """Feature: My Feature
|
148
|
-
|
149
|
-
Background:
|
150
|
-
Given something happens
|
151
|
-
And something cooler happens
|
152
|
-
|
153
|
-
Scenario: something else happens
|
154
|
-
Given foo
|
155
|
-
Then bar
|
156
|
-
"""
|
157
|
-
result = parser.parse(scenario)
|
158
|
-
|
159
|
-
result[:feature][:name].must_equal 'My Feature'
|
160
|
-
|
161
|
-
result[:feature][:background][:steps][0][:step][:name].must_equal 'something happens'
|
162
|
-
result[:feature][:background][:steps][1][:step][:name].must_equal 'something cooler happens'
|
163
31
|
|
164
|
-
|
165
|
-
result
|
166
|
-
result
|
32
|
+
it 'generates a nice tree' do
|
33
|
+
@result.must_be_kind_of AST::Feature
|
34
|
+
@result.line.must_equal 1
|
35
|
+
|
36
|
+
background = @result.background
|
37
|
+
background.must_be_kind_of AST::Background
|
38
|
+
background.line.must_equal 8
|
39
|
+
background.steps.first.keyword.must_equal 'Given'
|
40
|
+
background.steps.first.name.must_equal 'something happens before anything else happens'
|
41
|
+
background.steps.first.line.must_equal 9
|
42
|
+
background.steps.last.keyword.must_equal 'And'
|
43
|
+
background.steps.last.name.must_equal 'more things happens before anything else happens'
|
44
|
+
background.steps.last.line.must_equal 10
|
45
|
+
|
46
|
+
first_scenario = @result.scenarios.first
|
47
|
+
first_scenario.must_be_kind_of AST::Scenario
|
48
|
+
first_scenario.line.must_equal 13
|
49
|
+
first_scenario.name.must_equal 'something happens'
|
50
|
+
first_scenario.steps.first.keyword.must_equal 'Given'
|
51
|
+
first_scenario.steps.first.name.must_equal 'something happens'
|
52
|
+
first_scenario.steps.first.line.must_equal 14
|
53
|
+
first_scenario.steps.last.keyword.must_equal 'Then'
|
54
|
+
first_scenario.steps.last.name.must_equal 'something cooler happens'
|
55
|
+
first_scenario.steps.last.line.must_equal 15
|
56
|
+
|
57
|
+
last_scenario = @result.scenarios.last
|
58
|
+
last_scenario.must_be_kind_of AST::Scenario
|
59
|
+
last_scenario.line.must_equal 18
|
60
|
+
last_scenario.name.must_equal 'something else happens'
|
61
|
+
|
62
|
+
last_scenario.tags.first.name.must_equal 'javascript'
|
63
|
+
last_scenario.tags.last.name.must_equal 'wip'
|
64
|
+
|
65
|
+
last_scenario.steps.first.keyword.must_equal 'Given'
|
66
|
+
last_scenario.steps.first.name.must_equal 'foo'
|
67
|
+
last_scenario.steps.first.line.must_equal 19
|
68
|
+
last_scenario.steps.last.keyword.must_equal 'Then'
|
69
|
+
last_scenario.steps.last.name.must_equal 'bar'
|
70
|
+
last_scenario.steps.last.line.must_equal 20
|
167
71
|
end
|
168
72
|
end
|
169
73
|
end
|
metadata
CHANGED
@@ -1,47 +1,77 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: gherkin-ruby
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 1322991910812937300
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
- 0
|
10
|
+
version: 0.1.0
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Marc Divins
|
9
14
|
- Josep M. Bach
|
10
15
|
autorequire:
|
11
16
|
bindir: bin
|
12
17
|
cert_chain: []
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
+
|
19
|
+
date: 2011-11-30 00:00:00 +01:00
|
20
|
+
default_executable:
|
21
|
+
dependencies:
|
22
|
+
- !ruby/object:Gem::Dependency
|
23
|
+
name: rexical
|
24
|
+
prerelease: false
|
25
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
18
26
|
none: false
|
19
|
-
requirements:
|
20
|
-
- -
|
21
|
-
- !ruby/object:Gem::Version
|
22
|
-
|
27
|
+
requirements:
|
28
|
+
- - ">="
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
hash: 2002549777813010636
|
31
|
+
segments:
|
32
|
+
- 0
|
33
|
+
version: "0"
|
23
34
|
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: racc
|
24
38
|
prerelease: false
|
25
|
-
|
26
|
-
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 2002549777813010636
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
50
|
+
- !ruby/object:Gem::Dependency
|
27
51
|
name: minitest
|
28
|
-
|
52
|
+
prerelease: false
|
53
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
29
54
|
none: false
|
30
|
-
requirements:
|
31
|
-
- -
|
32
|
-
- !ruby/object:Gem::Version
|
33
|
-
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
hash: 2002549777813010636
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
34
62
|
type: :development
|
35
|
-
|
36
|
-
version_requirements: *2156943540
|
63
|
+
version_requirements: *id003
|
37
64
|
description: Gherkin-ruby is a Gherkin parser in pure Ruby using Parslet
|
38
|
-
email:
|
65
|
+
email:
|
39
66
|
- marcdivc@gmail.com
|
40
67
|
- josep.m.bach@gmail.com
|
41
68
|
executables: []
|
69
|
+
|
42
70
|
extensions: []
|
71
|
+
|
43
72
|
extra_rdoc_files: []
|
44
|
-
|
73
|
+
|
74
|
+
files:
|
45
75
|
- .gitignore
|
46
76
|
- .travis.yml
|
47
77
|
- Gemfile
|
@@ -51,38 +81,53 @@ files:
|
|
51
81
|
- lib/gherkin.rb
|
52
82
|
- lib/gherkin/ast.rb
|
53
83
|
- lib/gherkin/parser.rb
|
54
|
-
- lib/gherkin/
|
84
|
+
- lib/gherkin/parser/gherkin.rex
|
85
|
+
- lib/gherkin/parser/gherkin.y
|
86
|
+
- lib/gherkin/parser/lexer.rb
|
87
|
+
- lib/gherkin/parser/parser.rb
|
55
88
|
- lib/gherkin/version.rb
|
56
89
|
- test/gherkin/ast_test.rb
|
90
|
+
- test/gherkin/parser/lexer_test.rb
|
91
|
+
- test/gherkin/parser/parser_test.rb
|
57
92
|
- test/gherkin/parser_test.rb
|
58
|
-
- test/gherkin/transform_test.rb
|
59
93
|
- test/test_helper.rb
|
94
|
+
has_rdoc: true
|
60
95
|
homepage: http://github.com/codegram/gherkin
|
61
96
|
licenses: []
|
97
|
+
|
62
98
|
post_install_message:
|
63
99
|
rdoc_options: []
|
64
|
-
|
100
|
+
|
101
|
+
require_paths:
|
65
102
|
- lib
|
66
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
104
|
none: false
|
68
|
-
requirements:
|
69
|
-
- -
|
70
|
-
- !ruby/object:Gem::Version
|
71
|
-
|
72
|
-
|
105
|
+
requirements:
|
106
|
+
- - ">="
|
107
|
+
- !ruby/object:Gem::Version
|
108
|
+
hash: 2002549777813010636
|
109
|
+
segments:
|
110
|
+
- 0
|
111
|
+
version: "0"
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
113
|
none: false
|
74
|
-
requirements:
|
75
|
-
- -
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
hash: 2002549777813010636
|
118
|
+
segments:
|
119
|
+
- 0
|
120
|
+
version: "0"
|
78
121
|
requirements: []
|
122
|
+
|
79
123
|
rubyforge_project: gherkin-ruby
|
80
|
-
rubygems_version: 1.
|
124
|
+
rubygems_version: 1.5.2
|
81
125
|
signing_key:
|
82
126
|
specification_version: 3
|
83
127
|
summary: Gherkin-ruby is a Gherkin parser in pure Ruby using Parslet
|
84
|
-
test_files:
|
128
|
+
test_files:
|
85
129
|
- test/gherkin/ast_test.rb
|
130
|
+
- test/gherkin/parser/lexer_test.rb
|
131
|
+
- test/gherkin/parser/parser_test.rb
|
86
132
|
- test/gherkin/parser_test.rb
|
87
|
-
- test/gherkin/transform_test.rb
|
88
133
|
- test/test_helper.rb
|
data/lib/gherkin/transform.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
module Gherkin
|
2
|
-
class Transform < Parslet::Transform
|
3
|
-
# Match feature with background
|
4
|
-
rule(
|
5
|
-
feature: {
|
6
|
-
name: simple(:name),
|
7
|
-
background: {
|
8
|
-
steps: subtree(:background_steps)
|
9
|
-
},
|
10
|
-
scenarios: subtree(:scenarios)
|
11
|
-
}
|
12
|
-
) { AST::Feature.new(name, scenarios, AST::Background.new(background_steps)) }
|
13
|
-
|
14
|
-
# Match feature without background
|
15
|
-
rule(
|
16
|
-
feature: {
|
17
|
-
name: simple(:name),
|
18
|
-
scenarios: subtree(:scenarios)
|
19
|
-
}
|
20
|
-
) { AST::Feature.new(name, scenarios, AST::Background.new([])) }
|
21
|
-
|
22
|
-
# Match scenarios without tags
|
23
|
-
rule(
|
24
|
-
scenario: {
|
25
|
-
name: simple(:name),
|
26
|
-
steps: subtree(:steps)
|
27
|
-
}
|
28
|
-
) { AST::Scenario.new(name, steps, []) }
|
29
|
-
|
30
|
-
# Match scenarios with tags
|
31
|
-
rule(
|
32
|
-
scenario: {
|
33
|
-
name: simple(:name),
|
34
|
-
steps: subtree(:steps),
|
35
|
-
tags: subtree(:tags)
|
36
|
-
}
|
37
|
-
) { AST::Scenario.new(name, steps, tags) }
|
38
|
-
|
39
|
-
rule(
|
40
|
-
step: {
|
41
|
-
name: simple(:name),
|
42
|
-
keyword: simple(:keyword),
|
43
|
-
}
|
44
|
-
) { AST::Step.new(name, keyword) }
|
45
|
-
|
46
|
-
rule(
|
47
|
-
tag: simple(:name)
|
48
|
-
) { AST::Tag.new(name) }
|
49
|
-
end
|
50
|
-
end
|
@@ -1,74 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
require 'parslet'
|
3
|
-
|
4
|
-
module Gherkin
|
5
|
-
describe 'Feature parsing' do
|
6
|
-
before do
|
7
|
-
@scenario = """Feature: My Feature
|
8
|
-
In order to do something
|
9
|
-
As a developer
|
10
|
-
I want to be happy
|
11
|
-
|
12
|
-
Background:
|
13
|
-
Given something happens before anything else happens
|
14
|
-
And more things happens before anything else happens
|
15
|
-
|
16
|
-
Scenario: something happens
|
17
|
-
Given something happens
|
18
|
-
Then something cooler happens
|
19
|
-
|
20
|
-
@javascript @wip
|
21
|
-
Scenario: something else happens
|
22
|
-
Given foo
|
23
|
-
Then bar
|
24
|
-
"""
|
25
|
-
|
26
|
-
parser = Gherkin::Parser.new
|
27
|
-
result = parser.parse(@scenario)
|
28
|
-
transform = Gherkin::Transform.new
|
29
|
-
@result = transform.apply(result)
|
30
|
-
end
|
31
|
-
|
32
|
-
it 'generates a nice tree' do
|
33
|
-
@result.must_be_kind_of AST::Feature
|
34
|
-
@result.line.must_equal 1
|
35
|
-
|
36
|
-
background = @result.background
|
37
|
-
background.must_be_kind_of AST::Background
|
38
|
-
background.line.must_equal 6
|
39
|
-
background.column.must_equal 3
|
40
|
-
background.steps.first.keyword.must_equal 'Given'
|
41
|
-
background.steps.first.name.must_equal 'something happens before anything else happens'
|
42
|
-
background.steps.first.line.must_equal 7
|
43
|
-
background.steps.last.keyword.must_equal 'And'
|
44
|
-
background.steps.last.name.must_equal 'more things happens before anything else happens'
|
45
|
-
background.steps.last.line.must_equal 8
|
46
|
-
|
47
|
-
first_scenario = @result.scenarios.first
|
48
|
-
first_scenario.must_be_kind_of AST::Scenario
|
49
|
-
first_scenario.line.must_equal 10
|
50
|
-
first_scenario.name.must_equal 'something happens'
|
51
|
-
first_scenario.steps.first.keyword.must_equal 'Given'
|
52
|
-
first_scenario.steps.first.name.must_equal 'something happens'
|
53
|
-
first_scenario.steps.first.line.must_equal 11
|
54
|
-
first_scenario.steps.last.keyword.must_equal 'Then'
|
55
|
-
first_scenario.steps.last.name.must_equal 'something cooler happens'
|
56
|
-
first_scenario.steps.last.line.must_equal 12
|
57
|
-
|
58
|
-
last_scenario = @result.scenarios.last
|
59
|
-
last_scenario.must_be_kind_of AST::Scenario
|
60
|
-
last_scenario.line.must_equal 15
|
61
|
-
last_scenario.name.must_equal 'something else happens'
|
62
|
-
|
63
|
-
last_scenario.tags.first.name.must_equal 'javascript'
|
64
|
-
last_scenario.tags.last.name.must_equal 'wip'
|
65
|
-
|
66
|
-
last_scenario.steps.first.keyword.must_equal 'Given'
|
67
|
-
last_scenario.steps.first.name.must_equal 'foo'
|
68
|
-
last_scenario.steps.first.line.must_equal 16
|
69
|
-
last_scenario.steps.last.keyword.must_equal 'Then'
|
70
|
-
last_scenario.steps.last.name.must_equal 'bar'
|
71
|
-
last_scenario.steps.last.line.must_equal 17
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|