rley 0.3.04 → 0.3.05

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -3
  3. data/CHANGELOG.md +3 -0
  4. data/Rakefile +30 -30
  5. data/examples/parsers/parsing_L0.rb +1 -1
  6. data/examples/parsers/parsing_L1.rb +1 -1
  7. data/examples/parsers/parsing_abc.rb +1 -1
  8. data/examples/parsers/parsing_ambig.rb +1 -1
  9. data/examples/parsers/parsing_another.rb +1 -1
  10. data/examples/parsers/parsing_b_expr.rb +1 -1
  11. data/examples/parsers/parsing_err_expr.rb +1 -1
  12. data/examples/parsers/parsing_groucho.rb +1 -1
  13. data/examples/parsers/parsing_right_recursive.rb +1 -1
  14. data/examples/parsers/parsing_tricky.rb +1 -1
  15. data/lib/rley/constants.rb +2 -2
  16. data/lib/rley/formatter/base_formatter.rb +0 -2
  17. data/lib/rley/formatter/debug.rb +0 -2
  18. data/lib/rley/formatter/json.rb +1 -3
  19. data/lib/rley/gfg/call_edge.rb +31 -30
  20. data/lib/rley/gfg/edge.rb +22 -23
  21. data/lib/rley/gfg/end_vertex.rb +22 -24
  22. data/lib/rley/gfg/epsilon_edge.rb +20 -21
  23. data/lib/rley/gfg/grm_flow_graph.rb +39 -39
  24. data/lib/rley/gfg/item_vertex.rb +16 -17
  25. data/lib/rley/gfg/non_terminal_vertex.rb +3 -4
  26. data/lib/rley/gfg/return_edge.rb +32 -31
  27. data/lib/rley/gfg/scan_edge.rb +25 -26
  28. data/lib/rley/gfg/shortcut_edge.rb +25 -26
  29. data/lib/rley/gfg/start_vertex.rb +0 -2
  30. data/lib/rley/gfg/vertex.rb +8 -8
  31. data/lib/rley/parse_forest_visitor.rb +113 -115
  32. data/lib/rley/parse_tree_visitor.rb +0 -2
  33. data/lib/rley/parser/base_parser.rb +27 -27
  34. data/lib/rley/parser/chart.rb +14 -14
  35. data/lib/rley/parser/dotted_item.rb +33 -33
  36. data/lib/rley/parser/earley_parser.rb +6 -6
  37. data/lib/rley/parser/gfg_chart.rb +8 -15
  38. data/lib/rley/parser/gfg_earley_parser.rb +15 -13
  39. data/lib/rley/parser/gfg_parsing.rb +26 -22
  40. data/lib/rley/parser/grm_items_builder.rb +3 -2
  41. data/lib/rley/parser/parse_entry.rb +3 -9
  42. data/lib/rley/parser/parse_entry_set.rb +14 -19
  43. data/lib/rley/parser/parse_entry_tracker.rb +56 -56
  44. data/lib/rley/parser/parse_forest_builder.rb +215 -214
  45. data/lib/rley/parser/parse_forest_factory.rb +57 -56
  46. data/lib/rley/parser/parse_state.rb +8 -11
  47. data/lib/rley/parser/parse_state_tracker.rb +56 -56
  48. data/lib/rley/parser/parse_tracer.rb +3 -3
  49. data/lib/rley/parser/parse_tree_builder.rb +10 -10
  50. data/lib/rley/parser/parse_walker_factory.rb +30 -33
  51. data/lib/rley/parser/parsing.rb +8 -8
  52. data/lib/rley/parser/state_set.rb +23 -26
  53. data/lib/rley/ptree/non_terminal_node.rb +1 -1
  54. data/lib/rley/ptree/token_range.rb +2 -2
  55. data/lib/rley/sppf/alternative_node.rb +32 -34
  56. data/lib/rley/sppf/composite_node.rb +27 -27
  57. data/lib/rley/sppf/epsilon_node.rb +26 -27
  58. data/lib/rley/sppf/leaf_node.rb +11 -12
  59. data/lib/rley/sppf/non_terminal_node.rb +37 -38
  60. data/lib/rley/sppf/sppf_node.rb +1 -1
  61. data/lib/rley/sppf/token_node.rb +29 -29
  62. data/lib/rley/syntax/grammar.rb +1 -3
  63. data/lib/rley/syntax/grammar_builder.rb +8 -8
  64. data/lib/rley/syntax/non_terminal.rb +2 -4
  65. data/lib/rley/syntax/production.rb +3 -3
  66. data/lib/rley/syntax/symbol_seq.rb +1 -1
  67. data/spec/rley/gfg/call_edge_spec.rb +50 -51
  68. data/spec/rley/gfg/edge_spec.rb +33 -33
  69. data/spec/rley/gfg/end_vertex_spec.rb +26 -27
  70. data/spec/rley/gfg/epsilon_edge_spec.rb +25 -25
  71. data/spec/rley/gfg/grm_flow_graph_spec.rb +1 -1
  72. data/spec/rley/gfg/item_vertex_spec.rb +3 -4
  73. data/spec/rley/gfg/return_edge_spec.rb +51 -51
  74. data/spec/rley/gfg/scan_edge_spec.rb +32 -30
  75. data/spec/rley/gfg/shortcut_edge_spec.rb +1 -1
  76. data/spec/rley/gfg/vertex_spec.rb +3 -3
  77. data/spec/rley/parse_forest_visitor_spec.rb +239 -238
  78. data/spec/rley/parser/dotted_item_spec.rb +1 -1
  79. data/spec/rley/parser/earley_parser_spec.rb +16 -16
  80. data/spec/rley/parser/gfg_earley_parser_spec.rb +30 -31
  81. data/spec/rley/parser/gfg_parsing_spec.rb +11 -10
  82. data/spec/rley/parser/grm_items_builder_spec.rb +2 -2
  83. data/spec/rley/parser/parse_entry_set_spec.rb +4 -4
  84. data/spec/rley/parser/parse_entry_spec.rb +0 -2
  85. data/spec/rley/parser/parse_forest_builder_spec.rb +82 -57
  86. data/spec/rley/parser/parse_forest_factory_spec.rb +84 -82
  87. data/spec/rley/parser/parse_walker_factory_spec.rb +10 -9
  88. data/spec/rley/parser/parsing_spec.rb +0 -1
  89. data/spec/rley/sppf/alternative_node_spec.rb +2 -2
  90. data/spec/rley/sppf/non_terminal_node_spec.rb +0 -1
  91. data/spec/rley/support/ambiguous_grammar_helper.rb +1 -1
  92. data/spec/rley/support/expectation_helper.rb +37 -36
  93. data/spec/rley/support/grammar_abc_helper.rb +17 -17
  94. data/spec/rley/support/grammar_b_expr_helper.rb +40 -39
  95. data/spec/rley/support/grammar_helper.rb +2 -1
  96. data/spec/rley/support/{grammar_L0_helper.rb → grammar_l0_helper.rb} +82 -81
  97. data/spec/rley/support/grammar_sppf_helper.rb +24 -25
  98. data/spec/rley/syntax/grammar_spec.rb +1 -1
  99. metadata +2 -2
@@ -1,115 +1,113 @@
1
- module Rley # This module is used as a namespace
2
- # A visitor class dedicated in the visit of a parse forest.
3
- # It combines the Visitor and Observer patterns.
4
- class ParseForestVisitor
5
- # Link to the parse forest to visit
6
- attr_reader(:pforest)
7
-
8
- # List of objects that subscribed to the visit event notification.
9
- attr_reader(:subscribers)
10
-
11
- # A Hash with pairs of the form: Node => node visit data
12
- attr_reader(:agenda)
13
-
14
- # Indicates the kind of forest traversal to perform: :post_order, :pre-order
15
- attr_reader(:traversal)
16
-
17
- # Build a visitor for the given pforest.
18
- # @param aParseForest [ParseForest] the parse tree to visit.
19
- def initialize(aParseForest, aTraversalStrategy = :post_order)
20
- @pforest = aParseForest
21
- @subscribers = []
22
- @traversal = aTraversalStrategy
23
- end
24
-
25
- public
26
-
27
- # Add a subscriber for the visit event notifications.
28
- # @param aSubscriber [Object]
29
- def subscribe(aSubscriber)
30
- subscribers << aSubscriber
31
- end
32
-
33
- # Remove the given object from the subscription list.
34
- # The object won't be notified of visit events.
35
- # @param aSubscriber [Object]
36
- def unsubscribe(aSubscriber)
37
- subscribers.delete_if { |entry| entry == aSubscriber }
38
- end
39
-
40
- # The signal to begin the visit of the parse forest.
41
- def start()
42
- pforest.accept(self)
43
- end
44
-
45
-
46
- # Visit event. The visitor is about to visit the pforest.
47
- # @param aParseForest [ParseForest] the pforest to visit.
48
- def start_visit_pforest(aParseForest)
49
- broadcast(:before_pforest, aParseForest)
50
- end
51
-
52
-
53
- # Visit event. The visitor is about to visit the given non terminal node.
54
- # @param aNonTerminalNode [NonTerminalNode] the node to visit.
55
- def visit_nonterminal(aNonTerminalNode)
56
- if @traversal == :post_order
57
- broadcast(:before_non_terminal, aNonTerminalNode)
58
- traverse_children(aNonTerminalNode)
59
- else
60
- traverse_children(aNonTerminalNode)
61
- broadcast(:before_non_terminal, aNonTerminalNode)
62
- end
63
- broadcast(:after_non_terminal, aNonTerminalNode)
64
- end
65
-
66
- # Visit event. The visitor is visiting the
67
- # given terminal node.
68
- # @param aTerminalNode [TerminalNode] the terminal to visit.
69
- def visit_terminal(aTerminalNode)
70
- broadcast(:before_terminal, aTerminalNode)
71
- broadcast(:after_terminal, aTerminalNode)
72
- end
73
-
74
-
75
- # Visit event. The visitor has completed its visit of the given
76
- # non-terminal node.
77
- # @param aNonTerminalNode [NonTerminalNode] the node to visit.
78
- def end_visit_nonterminal(aNonTerminalNode)
79
- broadcast(:after_non_terminal, aNonTerminalNode)
80
- end
81
-
82
- # Visit event. The visitor has completed the visit of the pforest.
83
- # @param aParseForest [ParseForest] the pforest to visit.
84
- def end_visit_pforest(aParseForest)
85
- broadcast(:after_pforest, aParseForest)
86
- end
87
-
88
- private
89
-
90
- # Visit event. The visitor is about to visit the children of a non
91
- # terminal node.
92
- # @param aParentNode [NonTeminalNode] the (non-terminal) parent node.
93
- def traverse_children(aParentNode)
94
- children = aParentNode.children
95
- broadcast(:before_children, aParentNode, children)
96
-
97
- # Let's proceed with the visit of children
98
- children.each { |a_node| a_node.accept(self) }
99
-
100
- broadcast(:after_children, aParentNode, children)
101
- end
102
-
103
- # Send a notification to all subscribers.
104
- # @param msg [Symbol] event to notify
105
- # @param args [Array] arguments of the notification.
106
- def broadcast(msg, *args)
107
- subscribers.each do |a_subscriber|
108
- next unless a_subscriber.respond_to?(msg)
109
- a_subscriber.send(msg, *args)
110
- end
111
- end
112
- end # class
113
- end # module
114
-
115
- # End of file
1
+ module Rley # This module is used as a namespace
2
+ # A visitor class dedicated in the visit of a parse forest.
3
+ # It combines the Visitor and Observer patterns.
4
+ class ParseForestVisitor
5
+ # Link to the parse forest to visit
6
+ attr_reader(:pforest)
7
+
8
+ # List of objects that subscribed to the visit event notification.
9
+ attr_reader(:subscribers)
10
+
11
+ # A Hash with pairs of the form: Node => node visit data
12
+ attr_reader(:agenda)
13
+
14
+ # Indicates the kind of forest traversal to perform: :post_order, :pre-order
15
+ attr_reader(:traversal)
16
+
17
+ # Build a visitor for the given pforest.
18
+ # @param aParseForest [ParseForest] the parse tree to visit.
19
+ def initialize(aParseForest, aTraversalStrategy = :post_order)
20
+ @pforest = aParseForest
21
+ @subscribers = []
22
+ @traversal = aTraversalStrategy
23
+ end
24
+
25
+ # Add a subscriber for the visit event notifications.
26
+ # @param aSubscriber [Object]
27
+ def subscribe(aSubscriber)
28
+ subscribers << aSubscriber
29
+ end
30
+
31
+ # Remove the given object from the subscription list.
32
+ # The object won't be notified of visit events.
33
+ # @param aSubscriber [Object]
34
+ def unsubscribe(aSubscriber)
35
+ subscribers.delete_if { |entry| entry == aSubscriber }
36
+ end
37
+
38
+ # The signal to begin the visit of the parse forest.
39
+ def start()
40
+ pforest.accept(self)
41
+ end
42
+
43
+
44
+ # Visit event. The visitor is about to visit the pforest.
45
+ # @param aParseForest [ParseForest] the pforest to visit.
46
+ def start_visit_pforest(aParseForest)
47
+ broadcast(:before_pforest, aParseForest)
48
+ end
49
+
50
+
51
+ # Visit event. The visitor is about to visit the given non terminal node.
52
+ # @param aNonTerminalNode [NonTerminalNode] the node to visit.
53
+ def visit_nonterminal(aNonTerminalNode)
54
+ if @traversal == :post_order
55
+ broadcast(:before_non_terminal, aNonTerminalNode)
56
+ traverse_children(aNonTerminalNode)
57
+ else
58
+ traverse_children(aNonTerminalNode)
59
+ broadcast(:before_non_terminal, aNonTerminalNode)
60
+ end
61
+ broadcast(:after_non_terminal, aNonTerminalNode)
62
+ end
63
+
64
+ # Visit event. The visitor is visiting the
65
+ # given terminal node.
66
+ # @param aTerminalNode [TerminalNode] the terminal to visit.
67
+ def visit_terminal(aTerminalNode)
68
+ broadcast(:before_terminal, aTerminalNode)
69
+ broadcast(:after_terminal, aTerminalNode)
70
+ end
71
+
72
+
73
+ # Visit event. The visitor has completed its visit of the given
74
+ # non-terminal node.
75
+ # @param aNonTerminalNode [NonTerminalNode] the node to visit.
76
+ def end_visit_nonterminal(aNonTerminalNode)
77
+ broadcast(:after_non_terminal, aNonTerminalNode)
78
+ end
79
+
80
+ # Visit event. The visitor has completed the visit of the pforest.
81
+ # @param aParseForest [ParseForest] the pforest to visit.
82
+ def end_visit_pforest(aParseForest)
83
+ broadcast(:after_pforest, aParseForest)
84
+ end
85
+
86
+ private
87
+
88
+ # Visit event. The visitor is about to visit the children of a non
89
+ # terminal node.
90
+ # @param aParentNode [NonTeminalNode] the (non-terminal) parent node.
91
+ def traverse_children(aParentNode)
92
+ children = aParentNode.children
93
+ broadcast(:before_children, aParentNode, children)
94
+
95
+ # Let's proceed with the visit of children
96
+ children.each { |a_node| a_node.accept(self) }
97
+
98
+ broadcast(:after_children, aParentNode, children)
99
+ end
100
+
101
+ # Send a notification to all subscribers.
102
+ # @param msg [Symbol] event to notify
103
+ # @param args [Array] arguments of the notification.
104
+ def broadcast(msg, *args)
105
+ subscribers.each do |a_subscriber|
106
+ next unless a_subscriber.respond_to?(msg)
107
+ a_subscriber.send(msg, *args)
108
+ end
109
+ end
110
+ end # class
111
+ end # module
112
+
113
+ # End of file
@@ -19,8 +19,6 @@ module Rley # This module is used as a namespace
19
19
  @traversal = aTraversalStrategy
20
20
  end
21
21
 
22
- public
23
-
24
22
  # Add a subscriber for the visit event notifications.
25
23
  # @param aSubscriber [Object]
26
24
  def subscribe(aSubscriber)
@@ -1,27 +1,27 @@
1
- require_relative '../syntax/grammar'
2
- require_relative 'grm_items_builder' # Use mix-in module
3
- require_relative 'parse_tracer'
4
- require_relative 'parsing'
5
-
6
- module Rley # This module is used as a namespace
7
- module Parser # This module is used as a namespace
8
- # Abstract class for Earley parser.
9
- class BaseParser
10
- include GrmItemsBuilder # Mix-in module for created dotted items of given grammar
11
-
12
- # The grammar of the language.
13
- attr_reader(:grammar)
14
-
15
- # The dotted items/rules for the productions of the grammar
16
- attr_reader(:dotted_items)
17
-
18
-
19
- def initialize(aGrammar)
20
- @grammar = aGrammar
21
- @dotted_items = build_dotted_items(grammar) # Method from mixin
22
- end
23
- end # class
24
- end # module
25
- end # module
26
-
27
- # End of file
1
+ require_relative '../syntax/grammar'
2
+ require_relative 'grm_items_builder' # Use mix-in module
3
+ require_relative 'parse_tracer'
4
+ require_relative 'parsing'
5
+
6
+ module Rley # This module is used as a namespace
7
+ module Parser # This module is used as a namespace
8
+ # Abstract class for Earley parser.
9
+ class BaseParser
10
+ include GrmItemsBuilder # Mix-in for creating dotted items of grammar
11
+
12
+ # The grammar of the language.
13
+ attr_reader(:grammar)
14
+
15
+ # The dotted items/rules for the productions of the grammar
16
+ attr_reader(:dotted_items)
17
+
18
+
19
+ def initialize(aGrammar)
20
+ @grammar = aGrammar
21
+ @dotted_items = build_dotted_items(grammar) # Method from mixin
22
+ end
23
+ end # class
24
+ end # module
25
+ end # module
26
+
27
+ # End of file
@@ -8,7 +8,7 @@ module Rley # This module is used as a namespace
8
8
  class Chart
9
9
  # An array of state sets (one per input token + 1)
10
10
  attr_reader(:state_sets)
11
-
11
+
12
12
  # The level of trace details reported on stdout during the parse.
13
13
  # The possible values are:
14
14
  # 0: No trace output (default case)
@@ -16,10 +16,10 @@ module Rley # This module is used as a namespace
16
16
  # 2: Same as of 1 with the addition of the prediction rules
17
17
  attr_reader(:tracer)
18
18
 
19
- # @param startItems [Array] A non-empty Array of dotted items for
19
+ # @param startItems [Array] A non-empty Array of dotted items for
20
20
  # the start symbol.
21
21
  # @param tokenCount [Fixnum] The number of lexemes in the input to parse.
22
- # @param aTracer [ParseTracer] A tracer object.
22
+ # @param aTracer [ParseTracer] A tracer object.
23
23
  def initialize(startItems, tokenCount, aTracer)
24
24
  @tracer = aTracer
25
25
  @state_sets = Array.new(tokenCount + 1) { |_| StateSet.new }
@@ -34,7 +34,7 @@ module Rley # This module is used as a namespace
34
34
  def start_dotted_rule()
35
35
  return self[0].states.first.dotted_rule
36
36
  end
37
-
37
+
38
38
  # Return the start (non-terminal) symbol of the grammar.
39
39
  def start_symbol()
40
40
  return state_sets.first.states[0].dotted_rule.lhs
@@ -44,16 +44,16 @@ module Rley # This module is used as a namespace
44
44
  def [](index)
45
45
  return state_sets[index]
46
46
  end
47
-
47
+
48
48
  # Return the index value of the last non-empty state set.
49
49
  def last_index()
50
50
  first_empty = state_sets.find_index(&:empty?)
51
- if first_empty.nil?
52
- index = state_sets.size - 1
53
- else
54
- index = first_empty == 0 ? 0 : first_empty - 1
55
- end
56
-
51
+ index = if first_empty.nil?
52
+ state_sets.size - 1
53
+ else
54
+ first_empty.zero? ? 0 : first_empty - 1
55
+ end
56
+
57
57
  return index
58
58
  end
59
59
 
@@ -65,14 +65,14 @@ module Rley # This module is used as a namespace
65
65
  case aReason
66
66
  when :start_rule, :prediction
67
67
  tracer.trace_prediction(anIndex, new_state)
68
-
68
+
69
69
  when :scanning
70
70
  tracer.trace_scanning(anIndex, new_state)
71
-
71
+
72
72
  when :completion
73
73
  tracer.trace_completion(anIndex, new_state)
74
74
  else
75
- fail NotImplementedError, "Unknown push_state mode #{aReason}"
75
+ raise NotImplementedError, "Unknown push_state mode #{aReason}"
76
76
  end
77
77
  end
78
78
  end # class
@@ -6,7 +6,7 @@ module Rley # This module is used as a namespace
6
6
  # The right part consists of symbols that are predicted to match the
7
7
  # input tokens.
8
8
  # The terminology stems from the traditional way to visualize the partition
9
- # by using a fat dot character as a separator between the left and right
9
+ # by using a fat dot character as a separator between the left and right
10
10
  # parts
11
11
  # An item with the dot at the beginning (i.e. before any rhs symbol)
12
12
  # is called a predicted item.
@@ -29,7 +29,7 @@ module Rley # This module is used as a namespace
29
29
  @production = aProduction
30
30
  @position = valid_position(aPosition)
31
31
  end
32
-
32
+
33
33
  # Return a String representation of the dotted item.
34
34
  # @return [String]
35
35
  def to_s()
@@ -41,68 +41,68 @@ module Rley # This module is used as a namespace
41
41
  text_values.insert(position, '.')
42
42
  end
43
43
  suffix = text_values.join(' ')
44
-
44
+
45
45
  return prefix + suffix
46
46
  end
47
-
47
+
48
48
  # Return true if the dot position is at the start of the rhs.
49
49
  def at_start?()
50
- return position == 0 || position == -2
50
+ return position.zero? || position == -2
51
51
  end
52
52
 
53
- # An item with the dot at the beginning is called
54
- # predicted item
55
- alias_method :predicted_item?, :at_start?
53
+ # An item with the dot at the beginning is called
54
+ # predicted item
55
+ alias predicted_item? at_start?
56
56
 
57
57
  # A dotted item is called a reduce item if the dot is at the end.
58
58
  def reduce_item?()
59
59
  return position < 0 # Either -1 or -2
60
60
  end
61
-
61
+
62
62
  # The non-terminal symbol that is on the left-side of the production
63
63
  def lhs()
64
64
  return production.lhs
65
65
  end
66
-
66
+
67
67
  # Return the symbol before the dot.
68
68
  # nil is returned if the dot is at the start of the rhs
69
69
  def prev_symbol()
70
70
  before_position = prev_position
71
- if before_position.nil?
72
- result = nil
73
- else
74
- result = production.rhs[before_position]
75
- end
76
-
71
+ result = if before_position.nil?
72
+ nil
73
+ else
74
+ production.rhs[before_position]
75
+ end
76
+
77
77
  return result
78
78
  end
79
-
79
+
80
80
  # Return the symbol after the dot.
81
81
  # nil is returned if the dot is at the end
82
82
  def next_symbol()
83
- return (position < 0) ? nil : production.rhs[position]
83
+ return position < 0 ? nil : production.rhs[position]
84
84
  end
85
-
86
- # Calculate the position of the dot if were moved by
85
+
86
+ # Calculate the position of the dot if were moved by
87
87
  # one step on the left.
88
88
  def prev_position()
89
89
  case position
90
90
  when -2, 0
91
91
  result = nil
92
92
  when -1
93
- result = (production.rhs.size == 1) ? 0 : (production.rhs.size - 1)
93
+ result = production.rhs.size == 1 ? 0 : production.rhs.size - 1
94
94
  else
95
95
  result = position - 1
96
96
  end
97
-
97
+
98
98
  return result
99
99
  end
100
100
 
101
101
  # An item with the dot in front of a terminal is called a shift item
102
102
  def shift_item?()
103
- return position == 0
103
+ return position.zero?
104
104
  end
105
-
105
+
106
106
  # Return true if this dotted item has a dot one place
107
107
  # to the right compared to the dotted item argument.
108
108
  def successor_of?(another)
@@ -111,7 +111,7 @@ module Rley # This module is used as a namespace
111
111
  return false if to_the_left.nil?
112
112
  return to_the_left == another.position
113
113
  end
114
-
114
+
115
115
 
116
116
  private
117
117
 
@@ -119,16 +119,16 @@ module Rley # This module is used as a namespace
119
119
  def valid_position(aPosition)
120
120
  rhs_size = production.rhs.size
121
121
  if aPosition < 0 || aPosition > rhs_size
122
- fail StandardError, 'Out of bound index'
122
+ raise StandardError, 'Out of bound index'
123
123
  end
124
124
 
125
- if rhs_size == 0
126
- index = -2 # Minus 2 at start/end of empty production
127
- elsif aPosition == rhs_size
128
- index = -1 # Minus 1 at end of non-empty production
129
- else
130
- index = aPosition
131
- end
125
+ index = if rhs_size.zero?
126
+ -2 # Minus 2 at start/end of empty production
127
+ elsif aPosition == rhs_size
128
+ -1 # Minus 1 at end of non-empty production
129
+ else
130
+ aPosition
131
+ end
132
132
 
133
133
  return index
134
134
  end