rley 0.2.15 → 0.3.00

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +4 -0
  3. data/lib/rley/constants.rb +1 -1
  4. data/lib/rley/gfg/call_edge.rb +30 -0
  5. data/lib/rley/gfg/edge.rb +4 -0
  6. data/lib/rley/gfg/end_vertex.rb +1 -1
  7. data/lib/rley/gfg/epsilon_edge.rb +0 -4
  8. data/lib/rley/gfg/grm_flow_graph.rb +32 -7
  9. data/lib/rley/gfg/item_vertex.rb +71 -25
  10. data/lib/rley/gfg/non_terminal_vertex.rb +10 -1
  11. data/lib/rley/gfg/return_edge.rb +31 -0
  12. data/lib/rley/gfg/scan_edge.rb +2 -1
  13. data/lib/rley/gfg/shortcut_edge.rb +26 -0
  14. data/lib/rley/gfg/start_vertex.rb +2 -2
  15. data/lib/rley/gfg/vertex.rb +27 -1
  16. data/lib/rley/parse_forest_visitor.rb +115 -0
  17. data/lib/rley/parser/base_parser.rb +27 -0
  18. data/lib/rley/parser/dotted_item.rb +11 -0
  19. data/lib/rley/parser/earley_parser.rb +3 -15
  20. data/lib/rley/parser/gfg_chart.rb +106 -0
  21. data/lib/rley/parser/gfg_earley_parser.rb +139 -0
  22. data/lib/rley/parser/gfg_parsing.rb +384 -0
  23. data/lib/rley/parser/parse_entry.rb +148 -0
  24. data/lib/rley/parser/parse_entry_set.rb +104 -0
  25. data/lib/rley/parser/parse_entry_tracker.rb +56 -0
  26. data/lib/rley/parser/parse_forest_builder.rb +229 -0
  27. data/lib/rley/parser/parse_forest_factory.rb +54 -0
  28. data/lib/rley/parser/parse_walker_factory.rb +237 -0
  29. data/lib/rley/ptree/token_range.rb +14 -1
  30. data/lib/rley/sppf/alternative_node.rb +34 -0
  31. data/lib/rley/sppf/composite_node.rb +27 -0
  32. data/lib/rley/sppf/epsilon_node.rb +27 -0
  33. data/lib/rley/sppf/leaf_node.rb +12 -0
  34. data/lib/rley/sppf/non_terminal_node.rb +38 -0
  35. data/lib/rley/sppf/parse_forest.rb +48 -0
  36. data/lib/rley/sppf/sppf_node.rb +24 -0
  37. data/lib/rley/sppf/token_node.rb +29 -0
  38. data/lib/rley/syntax/grammar_builder.rb +16 -12
  39. data/lib/rley/syntax/grm_symbol.rb +6 -0
  40. data/lib/rley/syntax/terminal.rb +5 -0
  41. data/spec/rley/gfg/call_edge_spec.rb +51 -0
  42. data/spec/rley/gfg/end_vertex_spec.rb +1 -0
  43. data/spec/rley/gfg/grm_flow_graph_spec.rb +24 -2
  44. data/spec/rley/gfg/item_vertex_spec.rb +75 -6
  45. data/spec/rley/gfg/non_terminal_vertex_spec.rb +14 -0
  46. data/spec/rley/gfg/return_edge_spec.rb +51 -0
  47. data/spec/rley/gfg/shortcut_edge_spec.rb +43 -0
  48. data/spec/rley/gfg/vertex_spec.rb +52 -37
  49. data/spec/rley/parse_forest_visitor_spec.rb +238 -0
  50. data/spec/rley/parser/dotted_item_spec.rb +29 -8
  51. data/spec/rley/parser/gfg_chart_spec.rb +138 -0
  52. data/spec/rley/parser/gfg_earley_parser_spec.rb +918 -0
  53. data/spec/rley/parser/gfg_parsing_spec.rb +565 -0
  54. data/spec/rley/parser/parse_entry_set_spec.rb +179 -0
  55. data/spec/rley/parser/parse_entry_spec.rb +208 -0
  56. data/spec/rley/parser/parse_forest_builder_spec.rb +382 -0
  57. data/spec/rley/parser/parse_forest_factory_spec.rb +81 -0
  58. data/spec/rley/parser/parse_walker_factory_spec.rb +235 -0
  59. data/spec/rley/parser/state_set_spec.rb +4 -0
  60. data/spec/rley/sppf/alternative_node_spec.rb +72 -0
  61. data/spec/rley/sppf/antecedence_graph.rb +87 -0
  62. data/spec/rley/sppf/forest_representation.rb +136 -0
  63. data/spec/rley/sppf/gfg_representation.rb +111 -0
  64. data/spec/rley/sppf/non_terminal_node_spec.rb +64 -0
  65. data/spec/rley/support/ambiguous_grammar_helper.rb +36 -36
  66. data/spec/rley/support/expectation_helper.rb +36 -0
  67. data/spec/rley/support/grammar_helper.rb +28 -0
  68. data/spec/rley/support/grammar_sppf_helper.rb +25 -0
  69. data/spec/rley/syntax/grammar_builder_spec.rb +5 -0
  70. data/spec/rley/syntax/non_terminal_spec.rb +4 -0
  71. data/spec/rley/syntax/terminal_spec.rb +4 -0
  72. metadata +58 -2
@@ -0,0 +1,48 @@
1
+ require_relative 'token_node'
2
+ require_relative 'non_terminal_node'
3
+
4
+ module Rley # This module is used as a namespace
5
+ module SPPF # This module is used as a namespace
6
+ # TODO change comment
7
+ # A parse tree (a.k.a. concrete syntax tree) is a tree-based representation
8
+ # for the parse that corresponds to the input text. In a parse tree,
9
+ # a node corresponds to a grammar symbol used during the parsing:
10
+ # - a leaf node maps to a terminal symbol occurring in
11
+ # the input, and
12
+ # - a intermediate node maps to a non-terminal node reduced
13
+ # during the parse.
14
+ # The root node corresponds to the main/start symbol of the grammar.
15
+ class ParseForest
16
+ # The root node of the forest
17
+ attr_reader(:root)
18
+
19
+ # A Hash with pairs of the kind node key => node
20
+ attr_reader(:key2node)
21
+
22
+ # @param theRootNode [ParseForestNode] The root node of the parse tree.
23
+ def initialize(theRootNode)
24
+ @root = theRootNode
25
+ @key2node = {}
26
+ end
27
+
28
+ # Returns true if the given node is present in the forest.
29
+ def include?(aNode)
30
+ return key2node.include?(aNode)
31
+ end
32
+
33
+
34
+ # Part of the 'visitee' role in the Visitor design pattern.
35
+ # A visitee is expected to accept the visit from a visitor object
36
+ # @param aVisitor [ParseForestVisitor] the visitor object
37
+ def accept(aVisitor)
38
+ aVisitor.start_visit_pforest(self)
39
+
40
+ # Let's proceed with the visit of nodes
41
+ root.accept(aVisitor) if root
42
+
43
+ aVisitor.end_visit_pforest(self)
44
+ end
45
+ end # class
46
+ end # module
47
+ end # module
48
+ # End of file
@@ -0,0 +1,24 @@
1
+ require_relative '../ptree/token_range'
2
+
3
+ module Rley # This module is used as a namespace
4
+ module SPPF # This module is used as a namespace
5
+ # Abstract class. The generalization for all kinds of nodes
6
+ # occurring in a shared packed parse forest.
7
+ class SPPFNode
8
+
9
+ # A range of indices for tokens matching this node.
10
+ attr_reader(:range)
11
+
12
+ def initialize(aRange)
13
+ @range = PTree::TokenRange.new(aRange)
14
+ end
15
+
16
+ # Return the origin (= lower bound of the range
17
+ # = position of first token matched by the symbol)
18
+ def origin()
19
+ return range.low
20
+ end
21
+ end # class
22
+ end # module
23
+ end # module
24
+ # End of file
@@ -0,0 +1,29 @@
1
+ require_relative 'leaf_node'
2
+
3
+ module Rley # This module is used as a namespace
4
+ module SPPF # This module is used as a namespace
5
+ # A node in a parse forest that matches exactly one
6
+ # token from the input
7
+ class TokenNode < LeafNode
8
+ attr_reader(:token)
9
+
10
+ # aPosition is the position of the token in the input stream.
11
+ def initialize(aToken, aPosition)
12
+ range = {low: aPosition, high: aPosition + 1}
13
+ super(range)
14
+ @token = aToken
15
+ end
16
+
17
+ # Emit a (formatted) string representation of the node.
18
+ # Mainly used for diagnosis/debugging purposes.
19
+ def to_string(indentation)
20
+ return "#{token.terminal.name}#{range.to_string(indentation)}"
21
+ end
22
+
23
+ def key()
24
+ @key ||= to_string(0)
25
+ end
26
+ end # class
27
+ end # module
28
+ end # module
29
+ # End of file
@@ -67,21 +67,25 @@ module Rley # This module is used as a namespace
67
67
  end
68
68
 
69
69
  # Given the grammar symbols and productions added to the builder,
70
- # build the resulting grammar.
70
+ # build the resulting grammar (if not yet done).
71
71
  def grammar()
72
- fail StandardError, 'No symbol found for grammar' if symbols.empty?
73
- if productions.empty?
74
- fail StandardError, 'No production found for grammar'
72
+ unless @grammar
73
+ fail StandardError, 'No symbol found for grammar' if symbols.empty?
74
+ if productions.empty?
75
+ fail StandardError, 'No production found for grammar'
76
+ end
77
+
78
+ # Check that each non-terminal appears at least once in lhs.
79
+ all_non_terminals = symbols.values.select { |s| s.is_a?(NonTerminal) }
80
+ all_non_terminals.each do |n_term|
81
+ next if productions.any? { |prod| n_term == prod.lhs }
82
+ fail StandardError, "Nonterminal #{n_term.name} not rewritten"
83
+ end
84
+
85
+ @grammar = Grammar.new(productions.dup)
75
86
  end
76
87
 
77
- # Check that each non-terminal appears at least once in lhs.
78
- all_non_terminals = symbols.values.select { |s| s.is_a?(NonTerminal) }
79
- all_non_terminals.each do |n_term|
80
- next if productions.any? { |prod| n_term == prod.lhs }
81
- fail StandardError, "Nonterminal #{n_term.name} not rewritten"
82
- end
83
-
84
- return Grammar.new(productions.dup)
88
+ return @grammar
85
89
  end
86
90
 
87
91
  private
@@ -12,6 +12,12 @@ module Rley # This module is used as a namespace
12
12
  @name = aName.dup
13
13
  end
14
14
 
15
+ # Return true iff the symbol is a terminal
16
+ def terminal?()
17
+ # Default implementation to override if necessary
18
+ return false
19
+ end
20
+
15
21
  # The String representation of the grammar symbol
16
22
  # @return [String]
17
23
  def to_s()
@@ -12,6 +12,11 @@ module Rley # This module is used as a namespace
12
12
  super(aName)
13
13
  end
14
14
 
15
+ # Return true iff the symbol is a terminal
16
+ def terminal?()
17
+ return true
18
+ end
19
+
15
20
  # @return [false] Return true if the symbol derives
16
21
  # the empty string. As terminal symbol corresponds to a input token
17
22
  # it is by definition non-nullable.
@@ -0,0 +1,51 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ require_relative '../../../lib/rley/syntax/terminal'
4
+ require_relative '../../../lib/rley/syntax/non_terminal'
5
+ require_relative '../../../lib/rley/syntax/production'
6
+ require_relative '../../../lib/rley/parser/dotted_item'
7
+ require_relative '../../../lib/rley/gfg/start_vertex'
8
+ require_relative '../../../lib/rley/gfg/item_vertex'
9
+
10
+ # Load the class under test
11
+ require_relative '../../../lib/rley/gfg/call_edge'
12
+
13
+ module Rley # Open this namespace to avoid module qualifier prefixes
14
+ module GFG # Open this namespace to avoid module qualifier prefixes
15
+ describe CallEdge do
16
+ # Factory method. Builds a production with given left-hand side (LHS)
17
+ # and given RHS (right-hand side)
18
+ def build_prod(theLHS, *theRHSSymbols)
19
+ return Syntax::Production.new(theLHS, theRHSSymbols)
20
+ end
21
+
22
+ let(:t_a) { Rley::Syntax::Terminal.new('a') }
23
+ let(:t_b) { Rley::Syntax::Terminal.new('b') }
24
+ let(:t_c) { Rley::Syntax::Terminal.new('c') }
25
+ let(:nt_sentence) { Rley::Syntax::NonTerminal.new('sentence') }
26
+ let(:nt_b_sequence) { Rley::Syntax::NonTerminal.new('b_sequence') }
27
+ let(:sample_prod) { build_prod(nt_sentence, t_a, nt_b_sequence, t_c) }
28
+ let(:sample_item) { Parser::DottedItem.new(sample_prod, 1) }
29
+
30
+ let(:vertex1) { ItemVertex.new(sample_item) }
31
+ let(:vertex2) { StartVertex.new('to') }
32
+ subject { CallEdge.new(vertex1, vertex2) }
33
+
34
+ context 'Initialization:' do
35
+ it 'should be created with two vertice arguments' do
36
+ expect { CallEdge.new(vertex1, vertex2) }.not_to raise_error
37
+ end
38
+ end # context
39
+
40
+
41
+ context 'Provided services:' do
42
+ it 'should know its key' do
43
+ expectation = "CALL_#{sample_prod.object_id}_#{sample_item.position}"
44
+ expect(subject.key).to eq(expectation)
45
+ end
46
+ end # context
47
+ end # describe
48
+ end # module
49
+ end # module
50
+
51
+ # End of file
@@ -18,6 +18,7 @@ module Rley # Open this namespace to avoid module qualifier prefixes
18
18
  allow(sample_nt).to receive(:to_s).and_return('NT')
19
19
  expect(subject.label).to eq('NT.')
20
20
  end
21
+
21
22
  end # context
22
23
  end # describe
23
24
  end # module
@@ -1,5 +1,4 @@
1
1
  require_relative '../../spec_helper'
2
- require 'pp'
3
2
 
4
3
  require_relative '../support/grammar_abc_helper'
5
4
  require_relative '../../../lib/rley/parser/grm_items_builder'
@@ -53,6 +52,10 @@ module Rley # Open this namespace to avoid module qualifier prefixes
53
52
  expect { GrmFlowGraph.new(items_from_grammar) }.not_to raise_error
54
53
  end
55
54
 
55
+ it 'should know its main start vertex' do
56
+ expect(subject.start_vertex).to eq(subject.vertices.first)
57
+ end
58
+
56
59
  it 'should have the correct number of vertices' do
57
60
  # Number of vertices = count of dotted items +...
58
61
  # ... 2 * count of non-terminals
@@ -128,9 +131,28 @@ module Rley # Open this namespace to avoid module qualifier prefixes
128
131
  'A => a . --> A.',
129
132
  'A => . --> A.'
130
133
  ]
131
-
134
+
132
135
  compare_graph_expectations(graph, expected)
133
136
  end
137
+
138
+ it 'should have shortcut edges' do
139
+ subject.vertices.each do |a_vertex|
140
+ next unless a_vertex.kind_of?(ItemVertex)
141
+ if a_vertex.next_symbol.kind_of?(Syntax::NonTerminal)
142
+ expect(a_vertex.shortcut).not_to be_nil
143
+ my_d_item = a_vertex.dotted_item
144
+
145
+ # Retrieve dotted item of shortcut successor
146
+ other_d_item = a_vertex.shortcut.successor.dotted_item
147
+
148
+ # Now the checks...
149
+ expect(my_d_item.production).to eq(other_d_item.production)
150
+ expect(my_d_item.position).to eq(other_d_item.prev_position)
151
+ else
152
+ expect(a_vertex.shortcut).to be_nil
153
+ end
154
+ end
155
+ end
134
156
  end # context
135
157
  end # describe
136
158
  end # module
@@ -3,6 +3,7 @@ require_relative '../../../lib/rley/syntax/terminal'
3
3
  require_relative '../../../lib/rley/syntax/non_terminal'
4
4
  require_relative '../../../lib/rley/syntax/production'
5
5
  require_relative '../../../lib/rley/parser/dotted_item'
6
+ require_relative '../../../lib/rley/gfg/shortcut_edge'
6
7
 
7
8
  # Load the class under test
8
9
  require_relative '../../../lib/rley/gfg/item_vertex'
@@ -10,20 +11,25 @@ require_relative '../../../lib/rley/gfg/item_vertex'
10
11
  module Rley # Open this namespace to avoid module qualifier prefixes
11
12
  module GFG # Open this namespace to avoid module qualifier prefixes
12
13
  describe ItemVertex do
14
+
13
15
  # Factory method. Builds a production with given left-hand side (LHS)
14
16
  # and given RHS (right-hand side)
15
17
  def build_prod(theLHS, *theRHSSymbols)
16
18
  return Syntax::Production.new(theLHS, theRHSSymbols)
17
19
  end
18
20
 
19
- let(:t_a) { Rley::Syntax::Terminal.new('A') }
20
- let(:t_b) { Rley::Syntax::Terminal.new('B') }
21
- let(:t_c) { Rley::Syntax::Terminal.new('C') }
21
+ let(:t_a) { Rley::Syntax::Terminal.new('a') }
22
+ let(:t_b) { Rley::Syntax::Terminal.new('b') }
23
+ let(:t_c) { Rley::Syntax::Terminal.new('c') }
22
24
  let(:nt_sentence) { Rley::Syntax::NonTerminal.new('sentence') }
23
- let(:sample_prod) { build_prod(nt_sentence, t_a, t_b, t_c) }
25
+ let(:nt_b_sequence) { Rley::Syntax::NonTerminal.new('b_sequence') }
26
+ let(:sample_prod) { build_prod(nt_sentence, t_a, nt_b_sequence, t_c) }
24
27
  let(:other_prod) { build_prod(nt_sentence, t_a) }
28
+ let(:recursive_prod) {build_prod(nt_b_sequence, nt_b_sequence, t_b )}
29
+ let(:b_prod) {build_prod(nt_b_sequence, t_b )}
25
30
  let(:empty_prod) { build_prod(nt_sentence) }
26
31
  let(:sample_item) { Parser::DottedItem.new(sample_prod, 1) }
32
+ let(:next_item) { Parser::DottedItem.new(sample_prod, 2) }
27
33
  subject { ItemVertex.new(sample_item) }
28
34
 
29
35
  context 'Initialization:' do
@@ -34,15 +40,78 @@ module Rley # Open this namespace to avoid module qualifier prefixes
34
40
  it 'should know its dotted item' do
35
41
  expect(subject.dotted_item).to eq(sample_item)
36
42
  end
43
+
44
+ it 'should not have any shortcut edge at start' do
45
+ expect(subject.shortcut).to be_nil
46
+ end
37
47
  end # context
38
-
48
+
39
49
  context 'Provided services:' do
40
50
  it 'should know its label' do
41
51
  expect(subject.label).to eq(sample_item.to_s)
42
52
  end
53
+
54
+ it 'should know the lhs of the production' do
55
+ expect(subject.lhs).to eq(nt_sentence)
56
+ end
57
+
58
+ it 'should know whether it has a dot at the end of the rhs' do
59
+ # Case: dot not at the end
60
+ expect(subject).not_to be_complete
61
+
62
+ # Case: dot at the end
63
+ instance1 = ItemVertex.new(Parser::DottedItem.new(sample_prod, 3))
64
+ expect(instance1).to be_complete
65
+
66
+ # Case: empty production
67
+ instance2 = ItemVertex.new(Parser::DottedItem.new(empty_prod, 0))
68
+ expect(instance2).to be_complete
69
+ end
70
+
71
+ it 'should know the previous symbol (if any) in the rhs' do
72
+ # Case: dot is after first symbol
73
+ instance1 = ItemVertex.new(sample_item)
74
+ expect(instance1.prev_symbol).to eq(t_a)
75
+
76
+ # Case: dot is after second or later symbol
77
+ instance2 = ItemVertex.new(next_item)
78
+ expect(instance2.prev_symbol).to eq(nt_b_sequence)
79
+
80
+ # Case: dot is at begin
81
+ instance3 = ItemVertex.new(Parser::DottedItem.new(sample_prod, 0))
82
+ expect(instance3.prev_symbol).to be_nil
83
+
84
+ # Case: empty production
85
+ instance4 = ItemVertex.new(Parser::DottedItem.new(empty_prod, 0))
86
+ expect(instance4.prev_symbol).to be_nil
87
+ end
43
88
 
44
89
 
45
- end # context
90
+ it 'should know the next symbol (if any) in the rhs' do
91
+ #Case: dot is not penultimate
92
+ expect(subject.next_symbol).to eq(nt_b_sequence)
93
+
94
+ # Case: dot is penultimate
95
+ instance1 = ItemVertex.new(next_item)
96
+ expect(instance1.next_symbol).to eq(t_c)
97
+
98
+ # Case: dot is at end
99
+ instance2 = ItemVertex.new(Parser::DottedItem.new(sample_prod, 3))
100
+ expect(instance2.next_symbol).to be_nil
101
+
102
+ # Case: empty production
103
+ instance3 = ItemVertex.new(Parser::DottedItem.new(empty_prod, 0))
104
+ expect(instance3.next_symbol).to be_nil
105
+ end
106
+
107
+ it 'should accept a shortcut edge' do
108
+ next_vertex = ItemVertex.new(next_item)
109
+
110
+ # The ShortcutEdge constructor invokes the shortcut setter
111
+ shortcut = ShortcutEdge.new(subject, next_vertex)
112
+ expect(subject.shortcut).to eq(shortcut)
113
+ end
114
+ end # context
46
115
  end # describe
47
116
  end # module
48
117
  end # module
@@ -17,6 +17,20 @@ module Rley # Open this namespace to avoid module qualifier prefixes
17
17
  it 'should know its non-terminal' do
18
18
  expect(subject.non_terminal).to eq(sample_nt)
19
19
  end
20
+
21
+
22
+ it 'should accept at more than one outgoing edge' do
23
+ edge1 = double('fake-edge1')
24
+ edge2 = double('fake-edge2')
25
+
26
+ expect { subject.add_edge(edge1) }.not_to raise_error
27
+ expect(subject.edges.size).to eq(1)
28
+ expect(subject.edges.last).to eq(edge1)
29
+
30
+ expect { subject.add_edge(edge2) }.not_to raise_error
31
+ expect(subject.edges.size).to eq(2)
32
+ expect(subject.edges.last).to eq(edge2)
33
+ end
20
34
  end # context
21
35
  end # describe
22
36
  end # module
@@ -0,0 +1,51 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ require_relative '../../../lib/rley/syntax/terminal'
4
+ require_relative '../../../lib/rley/syntax/non_terminal'
5
+ require_relative '../../../lib/rley/syntax/production'
6
+ require_relative '../../../lib/rley/parser/dotted_item'
7
+ require_relative '../../../lib/rley/gfg/end_vertex'
8
+ require_relative '../../../lib/rley/gfg/item_vertex'
9
+
10
+ # Load the class under test
11
+ require_relative '../../../lib/rley/gfg/return_edge'
12
+
13
+ module Rley # Open this namespace to avoid module qualifier prefixes
14
+ module GFG # Open this namespace to avoid module qualifier prefixes
15
+ describe ReturnEdge do
16
+ # Factory method. Builds a production with given left-hand side (LHS)
17
+ # and given RHS (right-hand side)
18
+ def build_prod(theLHS, *theRHSSymbols)
19
+ return Syntax::Production.new(theLHS, theRHSSymbols)
20
+ end
21
+
22
+ let(:t_a) { Rley::Syntax::Terminal.new('a') }
23
+ let(:t_b) { Rley::Syntax::Terminal.new('b') }
24
+ let(:t_c) { Rley::Syntax::Terminal.new('c') }
25
+ let(:nt_sentence) { Rley::Syntax::NonTerminal.new('sentence') }
26
+ let(:nt_b_sequence) { Rley::Syntax::NonTerminal.new('b_sequence') }
27
+ let(:sample_prod) { build_prod(nt_sentence, t_a, nt_b_sequence, t_c) }
28
+ let(:sample_item) { Parser::DottedItem.new(sample_prod, 1) }
29
+
30
+ let(:vertex1) { EndVertex.new('from') }
31
+ let(:vertex2) { ItemVertex.new(sample_item) }
32
+ subject { ReturnEdge.new(vertex1, vertex2) }
33
+
34
+ context 'Initialization:' do
35
+ it 'should be created with two vertice arguments' do
36
+ expect { ReturnEdge.new(vertex1, vertex2) }.not_to raise_error
37
+ end
38
+ end # context
39
+
40
+
41
+ context 'Provided services:' do
42
+ it 'should know its key' do
43
+ expectation = "RET_#{sample_prod.object_id}_#{sample_item.position - 1}"
44
+ expect(subject.key).to eq(expectation)
45
+ end
46
+ end # context
47
+ end # describe
48
+ end # module
49
+ end # module
50
+
51
+ # End of file