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
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a8824f31c8801d99d12566267a5e8cd95bb26c1
4
- data.tar.gz: d5a0887f857d5b0efe154f10a202932960bc2e9e
3
+ metadata.gz: 5b7bad0d01ea1fbe0656bf1765843288c1521b26
4
+ data.tar.gz: d7b96f2d154b172c0847b3a96eaf0b6422439db1
5
5
  SHA512:
6
- metadata.gz: cbf13dc6ac58dced26074ed1b2f9a4aab99b2834ea720743621d0cec7f0e0b17b7dc89e19956765b4921f4e1f4601b1858da92bcf0260b86c41de5c1c040579d
7
- data.tar.gz: fed291494d3178e6813f561ef301ac60fc88604c81c0f8b40ee8fa8639e20873cef29fc79d90b47993e228134b1dcf0a8502ad1c3336b7e91659cb3a0c006345
6
+ metadata.gz: 76fa3fcf95a3bb55ca2e13eb1b449032cb3030cf18c5ab114e2398aeec16401d8c2b738f9818831dd4360d143f24f1240ffd13b8f0c495dcb8f5c684675ec894
7
+ data.tar.gz: 52f7f640dfc96d9f78a9ee1a44da1fb81086d446b1196d9a422bb5f6e7312c0e6ea089ce595b78b3b4c1d51d4cf810b18cda26fefd2477f7b5190dc9dfb8bca3
@@ -1,3 +1,7 @@
1
+ ### 0.3.00 / 2016-10-23
2
+ * [CHANGE] Many new classes. The gem bundles a second parser that copes with ambiguous grammars.
3
+
4
+
1
5
  ### 0.2.15 / 2016-02-14
2
6
  * [CHANGE] A lot of internal changes. This is the last version before a Grammar Flow Graph-based parsing implementation.
3
7
 
@@ -3,7 +3,7 @@
3
3
 
4
4
  module Rley # Module used as a namespace
5
5
  # The version number of the gem.
6
- Version = '0.2.15'
6
+ Version = '0.3.00'
7
7
 
8
8
  # Brief description of the gem.
9
9
  Description = "Ruby implementation of the Earley's parsing algorithm"
@@ -0,0 +1,30 @@
1
+ require_relative 'edge'
2
+
3
+ module Rley # This module is used as a namespace
4
+ module GFG # This module is used as a namespace
5
+ # Specialization of an edge in a grammar flow graph
6
+ # that has a item vertex as its head (predecessor).
7
+ # and a start vertex (.X) as its tail (successor).
8
+ # Responsibilities:
9
+ # - To know the successor vertex (tail)
10
+ class CallEdge < Edge
11
+ attr_reader(:key)
12
+
13
+ # Pre-condition: thePredecessor is an ItemVertex
14
+ # Pre-condition: theSuccessor is an StartVertex
15
+ def initialize(thePredecessor, theSuccessor)
16
+ super(thePredecessor, theSuccessor)
17
+ do_set_key(thePredecessor, theSuccessor)
18
+ end
19
+
20
+ private
21
+ def do_set_key(thePredecessor, theSuccessor)
22
+ tail_d_item = thePredecessor.dotted_item
23
+ @key = "CALL_#{tail_d_item.production.object_id}_#{tail_d_item.position}"
24
+ end
25
+
26
+ end # class
27
+ end # module
28
+ end # module
29
+
30
+ # End of file
@@ -11,6 +11,10 @@ module Rley # This module is used as a namespace
11
11
  @successor = theSuccessor
12
12
  thePredecessor.add_edge(self)
13
13
  end
14
+
15
+ def to_s()
16
+ " --> #{successor.label}"
17
+ end
14
18
 
15
19
  end # class
16
20
  end # module
@@ -15,7 +15,7 @@ module Rley # This module is used as a namespace
15
15
 
16
16
  def label()
17
17
  return "#{non_terminal}."
18
- end
18
+ end
19
19
 
20
20
  end # class
21
21
  end # module
@@ -13,10 +13,6 @@ module Rley # This module is used as a namespace
13
13
  def initialize(thePredecessor, theSuccessor)
14
14
  super(thePredecessor, theSuccessor)
15
15
  end
16
-
17
- def to_s()
18
- " --> #{successor.label}"
19
- end
20
16
 
21
17
  end # class
22
18
  end # module
@@ -2,7 +2,10 @@ require_relative 'start_vertex'
2
2
  require_relative 'end_vertex'
3
3
  require_relative 'item_vertex'
4
4
  require_relative 'epsilon_edge'
5
+ require_relative 'call_edge'
6
+ require_relative 'return_edge'
5
7
  require_relative 'scan_edge'
8
+ require_relative 'shortcut_edge'
6
9
 
7
10
  module Rley # This module is used as a namespace
8
11
  module GFG # This module is used as a namespace
@@ -27,11 +30,18 @@ module Rley # This module is used as a namespace
27
30
 
28
31
  build_graph(theDottedItems)
29
32
  end
33
+
34
+ # Return the vertex with given vertex label.
35
+ def find_vertex(aVertexLabel)
36
+ vertices.find { |a_vertex| a_vertex.label == aVertexLabel }
37
+ end
30
38
 
31
39
  private
32
40
  def add_vertex(aVertex)
41
+ fail StandardError, 'GFG vertex cannot be nil' if aVertex.nil?
42
+
33
43
  # TODO: make setting of start vertex more robust
34
- start_vertex = aVertex if vertices.empty?
44
+ @start_vertex = aVertex if vertices.empty?
35
45
  vertices << aVertex
36
46
  end
37
47
 
@@ -54,7 +64,9 @@ private
54
64
  end
55
65
  end
56
66
  end
57
-
67
+
68
+ # For each non-terminal from the grammar, say N
69
+ # Add the .N and N. vertices to the graph
58
70
  def build_all_starts_ends(theDottedItems)
59
71
  productions_raw = theDottedItems.map(&:production)
60
72
  productions = productions_raw.uniq
@@ -98,9 +110,12 @@ private
98
110
  # Rule 5
99
111
  # add a call edge: N => α[1] .A α[n] -> .A
100
112
  # add a return edge: A. -> N => α[1] A. α[n]
113
+ # add a shortcut edge: ( N => α[1] .A α[n] ) -> ( N => α[1] A. α[n] )
101
114
  def augment_graph(theDottedItems, firstItemPos)
102
115
  production = theDottedItems[firstItemPos].production
103
116
  max_index = production.rhs.size + 1
117
+ prev_vertex = nil
118
+
104
119
  (0...max_index).each do |index|
105
120
  current_item = theDottedItems[firstItemPos+index]
106
121
  new_vertex = ItemVertex.new(current_item)
@@ -119,6 +134,12 @@ private
119
134
  build_call_return_edges(vertices[-2], new_vertex)
120
135
  end
121
136
  end
137
+
138
+ prev_symbol = current_item.prev_symbol
139
+ if prev_symbol && prev_symbol.kind_of?(Syntax::NonTerminal)
140
+ build_shortcut_edge(prev_vertex, new_vertex)
141
+ end
142
+ prev_vertex = new_vertex
122
143
  end
123
144
  end
124
145
 
@@ -147,18 +168,22 @@ private
147
168
  ScanEdge.new(fromVertex, toVertex, fromVertex.dotted_item.next_symbol)
148
169
  end
149
170
 
150
- def build_call_return_edges(call_vertex, return_vertex)
151
- nt_symbol = call_vertex.dotted_item.next_symbol
171
+ def build_call_return_edges(calling_vertex, return_vertex)
172
+ nt_symbol = calling_vertex.dotted_item.next_symbol
152
173
 
153
174
  # Retrieve corresponding start vertex
154
175
  start_vertex = start_vertex_for[nt_symbol]
155
- # Create an edge call vertex -> start vertex
156
- EpsilonEdge.new(call_vertex, start_vertex)
176
+ # Create an edge 'calling' vertex -> start vertex
177
+ CallEdge.new(calling_vertex, start_vertex)
157
178
 
158
179
  # Retrieve corresponding end vertex
159
180
  end_vertex = end_vertex_for[nt_symbol]
160
181
  # Create an edge end vertex -> return vertex
161
- EpsilonEdge.new(end_vertex, return_vertex)
182
+ ReturnEdge.new(end_vertex, return_vertex)
183
+ end
184
+
185
+ def build_shortcut_edge(fromVertex, toVertex)
186
+ ShortcutEdge.new(fromVertex, toVertex)
162
187
  end
163
188
  end # class
164
189
  end # module
@@ -1,26 +1,72 @@
1
- require_relative 'vertex'
2
-
3
- module Rley # This module is used as a namespace
4
- module GFG # This module is used as a namespace
5
- # TODO: modify definition
6
- # Represents a specialized vertex in a grammar flow graph
7
- # that is associated to a given dotted item.
8
- # Responsibilities (in addition to inherited ones):
9
- # - Know its related non-terminal symbol
10
- class ItemVertex < Vertex
11
- attr_reader :dotted_item
12
-
13
- def initialize(aDottedItem)
14
- super()
15
- @dotted_item = aDottedItem
16
- end
17
-
18
- def label()
19
- return "#{dotted_item}"
20
- end
21
-
22
- end # class
23
- end # module
24
- end # module
25
-
1
+ require_relative '../syntax/non_terminal'
2
+ require_relative 'vertex'
3
+
4
+
5
+ module Rley # This module is used as a namespace
6
+ module GFG # This module is used as a namespace
7
+ # TODO: modify definition
8
+ # Represents a specialized vertex in a grammar flow graph
9
+ # that is associated to a given dotted item.
10
+ # Responsibilities (in addition to inherited ones):
11
+ # - Know its related non-terminal symbol
12
+ class ItemVertex < Vertex
13
+ # Link to the dotted item object
14
+ attr_reader :dotted_item
15
+
16
+ # Optional link to a "shortcut" edge.
17
+ # Applicable only if the dotted expects a non-terminal symbol.
18
+ attr_reader :shortcut
19
+
20
+ def initialize(aDottedItem)
21
+ super()
22
+ @dotted_item = aDottedItem
23
+ end
24
+
25
+ # Set the "shortcut" edge.
26
+ def shortcut=(aShortcut)
27
+ unless aShortcut.kind_of?(ShortcutEdge)
28
+ fail StandardError, 'Invalid shortcut argument'
29
+ end
30
+ =begin
31
+ unless next_symbol && next_symbol.kind_of?(Syntax::NonTerminal)
32
+ fail StandardError, 'Invalid shortcut usage'
33
+ end
34
+
35
+ shortcut_d_item = aShortcut.successor.dotted_item
36
+ unless (dotted_item.production == shortcut_d_item.production) &&
37
+ (dotted_item.position == shortcut_d_item.prev_position)
38
+ fail StandardError, 'Shortcut refers to wrong vertex'
39
+ end
40
+ =end
41
+ @shortcut = aShortcut
42
+ end
43
+
44
+ def label()
45
+ return "#{dotted_item}"
46
+ end
47
+
48
+ # Returns true if the dotted item has a dot at the end of the production.
49
+ def complete?()
50
+ return dotted_item.reduce_item?
51
+ end
52
+
53
+ # Return the symbol before the dot else nil.
54
+ def prev_symbol()
55
+ return dotted_item.prev_symbol
56
+ end
57
+
58
+ # Return the symbol after the dot else nil.
59
+ def next_symbol()
60
+ return dotted_item.next_symbol
61
+ end
62
+
63
+ # Return the non-terminal symbol at the left-hand side of the production
64
+ def lhs()
65
+ return dotted_item.lhs
66
+ end
67
+
68
+ end # class
69
+ end # module
70
+ end # module
71
+
26
72
  # End of file
@@ -3,7 +3,8 @@ require_relative 'vertex'
3
3
  module Rley # This module is used as a namespace
4
4
  module GFG # This module is used as a namespace
5
5
  # Represents a specialized vertex in a grammar flow graph
6
- # that is associated to a given non-terminal symbol.
6
+ # that is associated to a given non-terminal symbol and
7
+ # that may have in-degree or out-degree > 1
7
8
  # Responsibilities (in addition to inherited ones):
8
9
  # - Know its related non-terminal symbol
9
10
  class NonTerminalVertex < Vertex
@@ -13,6 +14,14 @@ module Rley # This module is used as a namespace
13
14
  super()
14
15
  @non_terminal = aNonTerminal
15
16
  end
17
+
18
+ protected
19
+
20
+ # Validation method for adding an outgoing edge to the vertex.
21
+ # A start vertex may accept an indegree and outdegree greater than one
22
+ def check_add_edge(anEdge)
23
+ return anEdge
24
+ end
16
25
 
17
26
  end # class
18
27
  end # module
@@ -0,0 +1,31 @@
1
+ require_relative 'edge'
2
+
3
+ module Rley # This module is used as a namespace
4
+ module GFG # This module is used as a namespace
5
+ # Specialization of an edge in a grammar flow graph
6
+ # that has a end vertex (X.) as its head
7
+ # and an item vertex as its tail
8
+
9
+ # Responsibilities:
10
+ # - To know the successor vertex (tail)
11
+ class ReturnEdge < Edge
12
+ attr_reader(:key)
13
+
14
+ # Pre-condition: thePredecessor is an EndVertex
15
+ # Pre-condition: theSuccessor is an ItemVertex
16
+ def initialize(thePredecessor, theSuccessor)
17
+ super(thePredecessor, theSuccessor)
18
+ do_set_key(thePredecessor, theSuccessor)
19
+ end
20
+
21
+ private
22
+ def do_set_key(thePredecessor, theSuccessor)
23
+ tail_d_item = theSuccessor.dotted_item
24
+ @key = "RET_#{tail_d_item.production.object_id}_#{tail_d_item.prev_position}"
25
+ end
26
+
27
+ end # class
28
+ end # module
29
+ end # module
30
+
31
+ # End of file
@@ -2,7 +2,8 @@ require_relative 'edge'
2
2
 
3
3
  module Rley # This module is used as a namespace
4
4
  module GFG # This module is used as a namespace
5
- # Abstract class. Represents an edge in a grammar flow graph
5
+ # Specialization of an edge in a grammar flow graph
6
+ # that is taken as a consequence of a scan rule.
6
7
  # Responsibilities:
7
8
  # - To know the successor vertex
8
9
  class ScanEdge < Edge
@@ -0,0 +1,26 @@
1
+ require_relative 'edge'
2
+
3
+ module Rley # This module is used as a namespace
4
+ module GFG # This module is used as a namespace
5
+ # Abstract class. Represents an edge in a grammar flow graph
6
+ # Responsibilities:
7
+ # - To know the successor vertex
8
+ class ShortcutEdge < Edge
9
+ # The terminal symbol expected from the input stream
10
+ attr_reader :nonterminal
11
+
12
+ def initialize(thePredecessor, theSuccessor)
13
+ @successor = theSuccessor
14
+ @nonterminal = thePredecessor.next_symbol
15
+ thePredecessor.shortcut = self
16
+ end
17
+
18
+ def to_s()
19
+ " -#{nonterminal}-> #{successor.label}"
20
+ end
21
+
22
+ end # class
23
+ end # module
24
+ end # module
25
+
26
+ # End of file
@@ -12,10 +12,10 @@ module Rley # This module is used as a namespace
12
12
  def initialize(aNonTerminal)
13
13
  super(aNonTerminal)
14
14
  end
15
-
15
+
16
16
  def label()
17
17
  return ".#{non_terminal}"
18
- end
18
+ end
19
19
 
20
20
  end # class
21
21
  end # module
@@ -14,7 +14,33 @@ module Rley # This module is used as a namespace
14
14
 
15
15
  # Add an graph edge to this vertex
16
16
  def add_edge(anEdge)
17
- edges << anEdge
17
+ arrow = check_add_edge(anEdge)
18
+ edges << arrow
19
+ end
20
+
21
+ # Returns true iff the vertex corresponds to an dotted item that has its dot
22
+ # at the end of a production (i.e. is a reduced item).
23
+ def complete?()
24
+ return false # Default implementation
25
+ end
26
+
27
+ # Return the symbol before the dot else nil.
28
+ def prev_symbol()
29
+ return nil # Default implementation
30
+ end
31
+
32
+ # Return the symbol after the dot else nil.
33
+ def next_symbol()
34
+ return nil # Default implementation
35
+ end
36
+
37
+ protected
38
+ # Validation method for adding an outgoing edge to the vertex.
39
+ # Vertices will accept an indegree and outdegree of at most one
40
+ # unless this method is overridden in subclasses
41
+ def check_add_edge(anEdge)
42
+ fail StandardError, 'At most one edge accepted' unless edges.empty?
43
+ return anEdge
18
44
  end
19
45
 
20
46
  end # class
@@ -0,0 +1,115 @@
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